diff options
author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2023-07-17 15:37:56 +0200 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2023-07-17 15:37:57 +0200 |
commit | 6c7f27441d6af776a89147027c6f4a11c0162c64 (patch) | |
tree | fc6f19c0b243dd77de3f270e1c2e8a41be086531 | |
parent | Linux 6.5-rc2 (diff) | |
parent | drm/loongson: Remove a useless check in cursor_plane_atomic_async_check() (diff) | |
download | linux-6c7f27441d6af776a89147027c6f4a11c0162c64.tar.xz linux-6c7f27441d6af776a89147027c6f4a11c0162c64.zip |
Merge tag 'drm-misc-next-2023-07-13' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v6.6:
UAPI Changes:
* fbdev:
* Make fbdev userspace interfaces optional; only leaves the
framebuffer console active
* prime:
* Support dma-buf self-import for all drivers automatically: improves
support for many userspace compositors
Cross-subsystem Changes:
* backlight:
* Fix interaction with fbdev in several drivers
* base: Convert struct platform.remove to return void; part of a larger,
tree-wide effort
* dma-buf: Acquire reservation lock for mmap() in exporters; part
of an on-going effort to simplify locking around dma-bufs
* fbdev:
* Use Linux device instead of fbdev device in many places
* Use deferred-I/O helper macros in various drivers
* i2c: Convert struct i2c from .probe_new to .probe; part of a larger,
tree-wide effort
* video:
* Avoid including <linux/screen_info.h>
Core Changes:
* atomic:
* Improve logging
* prime:
* Remove struct drm_driver.gem_prime_mmap plus driver updates: all
drivers now implement this callback with drm_gem_prime_mmap()
* gem:
* Support execution contexts: provides locking over multiple GEM
objects
* ttm:
* Support init_on_free
* Swapout fixes
Driver Changes:
* accel:
* ivpu: MMU updates; Support debugfs
* ast:
* Improve device-model detection
* Cleanups
* bridge:
* dw-hdmi: Improve support for YUV420 bus format
* dw-mipi-dsi: Fix enable/disable of DSI controller
* lt9611uxc: Use MODULE_FIRMWARE()
* ps8640: Remove broken EDID code
* samsung-dsim: Fix command transfer
* tc358764: Handle HS/VS polarity; Use BIT() macro; Various cleanups
* Cleanups
* ingenic:
* Kconfig REGMAP fixes
* loongson:
* Support display controller
* mgag200:
* Minor fixes
* mxsfb:
* Support disabling overlay planes
* nouveau:
* Improve VRAM detection
* Various fixes and cleanups
* panel:
* panel-edp: Support AUO B116XAB01.4
* Support Visionox R66451 plus DT bindings
* Cleanups
* ssd130x:
* Support per-controller default resolution plus DT bindings
* Reduce memory-allocation overhead
* Cleanups
* tidss:
* Support TI AM625 plus DT bindings
* Implement new connector model plus driver updates
* vkms
* Improve write-back support
* Documentation fixes
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20230713090830.GA23281@linux-uq9g
370 files changed, 11462 insertions, 3303 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/visionox,r66451.yaml b/Documentation/devicetree/bindings/display/panel/visionox,r66451.yaml new file mode 100644 index 000000000000..6ba323683921 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/visionox,r66451.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/visionox,r66451.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Visionox R66451 AMOLED DSI Panel + +maintainers: + - Jessica Zhang <quic_jesszhan@quicinc.com> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: visionox,r66451 + + reg: + maxItems: 1 + description: DSI virtual channel + + vddio-supply: true + vdd-supply: true + port: true + reset-gpios: true + +additionalProperties: false + +required: + - compatible + - reg + - vddio-supply + - vdd-supply + - reset-gpios + - port + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "visionox,r66451"; + reg = <0>; + vddio-supply = <&vreg_l12c_1p8>; + vdd-supply = <&vreg_l13c_3p0>; + + reset-gpios = <&tlmm 24 GPIO_ACTIVE_LOW>; + + port { + panel0_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml b/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml index 94bb5ef567c6..20e2bd15d4d2 100644 --- a/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml +++ b/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml @@ -49,15 +49,15 @@ properties: solomon,height: $ref: /schemas/types.yaml#/definitions/uint32 - default: 16 description: - Height in pixel of the screen driven by the controller + Height in pixel of the screen driven by the controller. + The default value is controller-dependent. solomon,width: $ref: /schemas/types.yaml#/definitions/uint32 - default: 96 description: - Width in pixel of the screen driven by the controller + Width in pixel of the screen driven by the controller. + The default value is controller-dependent. solomon,page-offset: $ref: /schemas/types.yaml#/definitions/uint32 @@ -157,6 +157,10 @@ allOf: const: sinowealth,sh1106 then: properties: + width: + default: 132 + height: + default: 64 solomon,dclk-div: default: 1 solomon,dclk-frq: @@ -171,6 +175,10 @@ allOf: - solomon,ssd1305 then: properties: + width: + default: 132 + height: + default: 64 solomon,dclk-div: default: 1 solomon,dclk-frq: @@ -185,6 +193,10 @@ allOf: - solomon,ssd1306 then: properties: + width: + default: 128 + height: + default: 64 solomon,dclk-div: default: 1 solomon,dclk-frq: @@ -199,6 +211,10 @@ allOf: - solomon,ssd1307 then: properties: + width: + default: 128 + height: + default: 39 solomon,dclk-div: default: 2 solomon,dclk-frq: @@ -215,6 +231,10 @@ allOf: - solomon,ssd1309 then: properties: + width: + default: 128 + height: + default: 64 solomon,dclk-div: default: 1 solomon,dclk-frq: diff --git a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml index b6b402f16161..ae09cd3cbce1 100644 --- a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml +++ b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml @@ -12,14 +12,18 @@ maintainers: - Tomi Valkeinen <tomi.valkeinen@ti.com> description: | - The AM65x TI Keystone Display SubSystem with two output ports and - two video planes. The first video port supports OLDI and the second - supports DPI format. The fist plane is full video plane with all - features and the second is a "lite plane" without scaling support. + The AM625 and AM65x TI Keystone Display SubSystem with two output + ports and two video planes. In AM65x DSS, the first video port + supports 1 OLDI TX and in AM625 DSS, the first video port output is + internally routed to 2 OLDI TXes. The second video port supports DPI + format. The first plane is full video plane with all features and the + second is a "lite plane" without scaling support. properties: compatible: - const: ti,am65x-dss + enum: + - ti,am625-dss + - ti,am65x-dss reg: description: @@ -80,7 +84,9 @@ properties: port@0: $ref: /schemas/graph.yaml#/properties/port description: - The DSS OLDI output port node form video port 1 + For AM65x DSS, the OLDI output port node from video port 1. + For AM625 DSS, the internal DPI output port node from video + port 1. port@1: $ref: /schemas/graph.yaml#/properties/port diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index a79fd3549ff8..a52e6f4117d6 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -493,6 +493,18 @@ DRM Sync Objects .. kernel-doc:: drivers/gpu/drm/drm_syncobj.c :export: +DRM Execution context +===================== + +.. kernel-doc:: drivers/gpu/drm/drm_exec.c + :doc: Overview + +.. kernel-doc:: include/drm/drm_exec.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_exec.c + :export: + GPU Scheduler ============= diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 68bdafa0284f..d0f9f87ea515 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -319,15 +319,6 @@ Contact: Daniel Vetter, Noralf Tronnes Level: Advanced -struct drm_gem_object_funcs ---------------------------- - -GEM objects can now have a function table instead of having the callbacks on the -DRM driver struct. This is now the preferred way. Callbacks in drivers have been -converted, except for struct drm_driver.gem_prime_mmap. - -Level: Intermediate - connector register/unregister fixes ----------------------------------- @@ -452,6 +443,19 @@ Contact: Thomas Zimmermann <tzimmermann@suse.de> Level: Starter +Remove driver dependencies on FB_DEVICE +--------------------------------------- + +A number of fbdev drivers provide attributes via sysfs and therefore depend +on CONFIG_FB_DEVICE to be selected. Review each driver and attempt to make +any dependencies on CONFIG_FB_DEVICE optional. At the minimum, the respective +code in the driver could be conditionalized via ifdef CONFIG_FB_DEVICE. Not +all drivers might be able to drop CONFIG_FB_DEVICE. + +Contact: Thomas Zimmermann <tzimmermann@suse.de> + +Level: Starter + Core refactorings ================= diff --git a/MAINTAINERS b/MAINTAINERS index aee340630eca..cd882b87a3c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6150,10 +6150,9 @@ F: kernel/dma/ DMA-BUF HEAPS FRAMEWORK M: Sumit Semwal <sumit.semwal@linaro.org> R: Benjamin Gaignard <benjamin.gaignard@collabora.com> -R: Liam Mark <lmark@codeaurora.org> -R: Laura Abbott <labbott@redhat.com> R: Brian Starkey <Brian.Starkey@arm.com> R: John Stultz <jstultz@google.com> +R: T.J. Mercier <tjmercier@google.com> L: linux-media@vger.kernel.org L: dri-devel@lists.freedesktop.org L: linaro-mm-sig@lists.linaro.org (moderated for non-subscribers) @@ -6395,6 +6394,7 @@ F: drivers/gpu/drm/aspeed/ DRM DRIVER FOR AST SERVER GRAPHICS CHIPS M: Dave Airlie <airlied@redhat.com> R: Thomas Zimmermann <tzimmermann@suse.de> +R: Jocelyn Falempe <jfalempe@redhat.com> L: dri-devel@lists.freedesktop.org S: Supported T: git git://anongit.freedesktop.org/drm/drm-misc @@ -6546,6 +6546,7 @@ F: drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c DRM DRIVER FOR MGA G200 GRAPHICS CHIPS M: Dave Airlie <airlied@redhat.com> R: Thomas Zimmermann <tzimmermann@suse.de> +R: Jocelyn Falempe <jfalempe@redhat.com> L: dri-devel@lists.freedesktop.org S: Supported T: git git://anongit.freedesktop.org/drm/drm-misc @@ -6945,6 +6946,13 @@ T: git git://anongit.freedesktop.org/drm/drm-misc F: drivers/gpu/drm/lima/ F: include/uapi/drm/lima_drm.h +DRM DRIVERS FOR LOONGSON +M: Sui Jingfeng <suijingfeng@loongson.cn> +L: dri-devel@lists.freedesktop.org +S: Supported +T: git git://anongit.freedesktop.org/drm/drm-misc +F: drivers/gpu/drm/loongson/ + DRM DRIVERS FOR MEDIATEK M: Chun-Kuang Hu <chunkuang.hu@kernel.org> M: Philipp Zabel <p.zabel@pengutronix.de> @@ -7014,7 +7022,7 @@ F: drivers/gpu/drm/stm DRM DRIVERS FOR TI KEYSTONE M: Jyri Sarha <jyri.sarha@iki.fi> -M: Tomi Valkeinen <tomba@kernel.org> +M: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> L: dri-devel@lists.freedesktop.org S: Maintained T: git git://anongit.freedesktop.org/drm/drm-misc @@ -7025,16 +7033,18 @@ F: drivers/gpu/drm/tidss/ DRM DRIVERS FOR TI LCDC M: Jyri Sarha <jyri.sarha@iki.fi> -R: Tomi Valkeinen <tomba@kernel.org> +M: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> L: dri-devel@lists.freedesktop.org S: Maintained +T: git git://anongit.freedesktop.org/drm/drm-misc F: Documentation/devicetree/bindings/display/tilcdc/ F: drivers/gpu/drm/tilcdc/ DRM DRIVERS FOR TI OMAP -M: Tomi Valkeinen <tomba@kernel.org> +M: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> L: dri-devel@lists.freedesktop.org S: Maintained +T: git git://anongit.freedesktop.org/drm/drm-misc F: Documentation/devicetree/bindings/display/ti/ F: drivers/gpu/drm/omapdrm/ diff --git a/arch/arm/kernel/efi.c b/arch/arm/kernel/efi.c index e2b9d2618c67..e94655ef16bb 100644 --- a/arch/arm/kernel/efi.c +++ b/arch/arm/kernel/efi.c @@ -5,6 +5,8 @@ #include <linux/efi.h> #include <linux/memblock.h> +#include <linux/screen_info.h> + #include <asm/efi.h> #include <asm/mach/map.h> #include <asm/mmu_context.h> diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index baab8dd3ead3..3afbe503b066 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -9,6 +9,7 @@ #include <linux/efi.h> #include <linux/init.h> +#include <linux/screen_info.h> #include <asm/efi.h> #include <asm/stacktrace.h> diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c index 3d448fef3af4..9fc10cea21e1 100644 --- a/arch/loongarch/kernel/efi.c +++ b/arch/loongarch/kernel/efi.c @@ -18,6 +18,7 @@ #include <linux/kobject.h> #include <linux/memblock.h> #include <linux/reboot.h> +#include <linux/screen_info.h> #include <linux/uaccess.h> #include <asm/early_ioremap.h> diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 674da7ebd8b7..310513646c9b 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -386,7 +386,7 @@ static struct property_entry gpio_backlight_props[] = { }; static struct gpio_backlight_platform_data gpio_backlight_data = { - .fbdev = &lcdc_device.dev, + .dev = &lcdc_device.dev, }; static const struct platform_device_info gpio_backlight_device_info = { diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c index 20f4db778ed6..a18e80394aed 100644 --- a/arch/sh/boards/mach-kfr2r09/setup.c +++ b/arch/sh/boards/mach-kfr2r09/setup.c @@ -202,7 +202,7 @@ static struct platform_device kfr2r09_sh_lcdc_device = { }; static struct lv5207lp_platform_data kfr2r09_backlight_data = { - .fbdev = &kfr2r09_sh_lcdc_device.dev, + .dev = &kfr2r09_sh_lcdc_device.dev, .def_value = 13, .max_value = 13, }; diff --git a/drivers/accel/ivpu/Makefile b/drivers/accel/ivpu/Makefile index 80f1fb3548ae..9858d9fea36e 100644 --- a/drivers/accel/ivpu/Makefile +++ b/drivers/accel/ivpu/Makefile @@ -2,8 +2,10 @@ # Copyright (C) 2023 Intel Corporation intel_vpu-y := \ + ivpu_debugfs.o \ ivpu_drv.o \ ivpu_fw.o \ + ivpu_fw_log.o \ ivpu_gem.o \ ivpu_hw_mtl.o \ ivpu_ipc.o \ @@ -13,4 +15,4 @@ intel_vpu-y := \ ivpu_mmu_context.o \ ivpu_pm.o -obj-$(CONFIG_DRM_ACCEL_IVPU) += intel_vpu.o
\ No newline at end of file +obj-$(CONFIG_DRM_ACCEL_IVPU) += intel_vpu.o diff --git a/drivers/accel/ivpu/ivpu_debugfs.c b/drivers/accel/ivpu/ivpu_debugfs.c new file mode 100644 index 000000000000..5e5996fd4f9f --- /dev/null +++ b/drivers/accel/ivpu/ivpu_debugfs.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2023 Intel Corporation + */ + +#include <drm/drm_debugfs.h> +#include <drm/drm_file.h> +#include <drm/drm_print.h> + +#include <uapi/drm/ivpu_accel.h> + +#include "ivpu_debugfs.h" +#include "ivpu_drv.h" +#include "ivpu_fw.h" +#include "ivpu_fw_log.h" +#include "ivpu_gem.h" +#include "ivpu_jsm_msg.h" +#include "ivpu_pm.h" + +static int bo_list_show(struct seq_file *s, void *v) +{ + struct drm_info_node *node = (struct drm_info_node *)s->private; + struct drm_printer p = drm_seq_file_printer(s); + + ivpu_bo_list(node->minor->dev, &p); + + return 0; +} + +static int fw_name_show(struct seq_file *s, void *v) +{ + struct drm_info_node *node = (struct drm_info_node *)s->private; + struct ivpu_device *vdev = to_ivpu_device(node->minor->dev); + + seq_printf(s, "%s\n", vdev->fw->name); + return 0; +} + +static int fw_trace_capability_show(struct seq_file *s, void *v) +{ + struct drm_info_node *node = (struct drm_info_node *)s->private; + struct ivpu_device *vdev = to_ivpu_device(node->minor->dev); + u64 trace_hw_component_mask; + u32 trace_destination_mask; + int ret; + + ret = ivpu_jsm_trace_get_capability(vdev, &trace_destination_mask, + &trace_hw_component_mask); + if (!ret) { + seq_printf(s, + "trace_destination_mask: %#18x\n" + "trace_hw_component_mask: %#18llx\n", + trace_destination_mask, trace_hw_component_mask); + } + return 0; +} + +static int fw_trace_config_show(struct seq_file *s, void *v) +{ + struct drm_info_node *node = (struct drm_info_node *)s->private; + struct ivpu_device *vdev = to_ivpu_device(node->minor->dev); + /** + * WA: VPU_JSM_MSG_TRACE_GET_CONFIG command is not working yet, + * so we use values from vdev->fw instead of calling ivpu_jsm_trace_get_config() + */ + u32 trace_level = vdev->fw->trace_level; + u32 trace_destination_mask = vdev->fw->trace_destination_mask; + u64 trace_hw_component_mask = vdev->fw->trace_hw_component_mask; + + seq_printf(s, + "trace_level: %#18x\n" + "trace_destination_mask: %#18x\n" + "trace_hw_component_mask: %#18llx\n", + trace_level, trace_destination_mask, trace_hw_component_mask); + + return 0; +} + +static int last_bootmode_show(struct seq_file *s, void *v) +{ + struct drm_info_node *node = (struct drm_info_node *)s->private; + struct ivpu_device *vdev = to_ivpu_device(node->minor->dev); + + seq_printf(s, "%s\n", (vdev->pm->is_warmboot) ? "warmboot" : "coldboot"); + + return 0; +} + +static int reset_counter_show(struct seq_file *s, void *v) +{ + struct drm_info_node *node = (struct drm_info_node *)s->private; + struct ivpu_device *vdev = to_ivpu_device(node->minor->dev); + + seq_printf(s, "%d\n", atomic_read(&vdev->pm->reset_counter)); + return 0; +} + +static int reset_pending_show(struct seq_file *s, void *v) +{ + struct drm_info_node *node = (struct drm_info_node *)s->private; + struct ivpu_device *vdev = to_ivpu_device(node->minor->dev); + + seq_printf(s, "%d\n", atomic_read(&vdev->pm->in_reset)); + return 0; +} + +static const struct drm_info_list vdev_debugfs_list[] = { + {"bo_list", bo_list_show, 0}, + {"fw_name", fw_name_show, 0}, + {"fw_trace_capability", fw_trace_capability_show, 0}, + {"fw_trace_config", fw_trace_config_show, 0}, + {"last_bootmode", last_bootmode_show, 0}, + {"reset_counter", reset_counter_show, 0}, + {"reset_pending", reset_pending_show, 0}, +}; + +static int fw_log_show(struct seq_file *s, void *v) +{ + struct ivpu_device *vdev = s->private; + struct drm_printer p = drm_seq_file_printer(s); + + ivpu_fw_log_print(vdev, true, &p); + return 0; +} + +static int fw_log_fops_open(struct inode *inode, struct file *file) +{ + return single_open(file, fw_log_show, inode->i_private); +} + +static ssize_t +fw_log_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) +{ + struct seq_file *s = file->private_data; + struct ivpu_device *vdev = s->private; + + if (!size) + return -EINVAL; + + ivpu_fw_log_clear(vdev); + return size; +} + +static const struct file_operations fw_log_fops = { + .owner = THIS_MODULE, + .open = fw_log_fops_open, + .write = fw_log_fops_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t +fw_trace_destination_mask_fops_write(struct file *file, const char __user *user_buf, + size_t size, loff_t *pos) +{ + struct ivpu_device *vdev = file->private_data; + struct ivpu_fw_info *fw = vdev->fw; + u32 trace_destination_mask; + int ret; + + ret = kstrtou32_from_user(user_buf, size, 0, &trace_destination_mask); + if (ret < 0) + return ret; + + fw->trace_destination_mask = trace_destination_mask; + + ivpu_jsm_trace_set_config(vdev, fw->trace_level, trace_destination_mask, + fw->trace_hw_component_mask); + + return size; +} + +static const struct file_operations fw_trace_destination_mask_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = fw_trace_destination_mask_fops_write, +}; + +static ssize_t +fw_trace_hw_comp_mask_fops_write(struct file *file, const char __user *user_buf, + size_t size, loff_t *pos) +{ + struct ivpu_device *vdev = file->private_data; + struct ivpu_fw_info *fw = vdev->fw; + u64 trace_hw_component_mask; + int ret; + + ret = kstrtou64_from_user(user_buf, size, 0, &trace_hw_component_mask); + if (ret < 0) + return ret; + + fw->trace_hw_component_mask = trace_hw_component_mask; + + ivpu_jsm_trace_set_config(vdev, fw->trace_level, fw->trace_destination_mask, + trace_hw_component_mask); + + return size; +} + +static const struct file_operations fw_trace_hw_comp_mask_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = fw_trace_hw_comp_mask_fops_write, +}; + +static ssize_t +fw_trace_level_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) +{ + struct ivpu_device *vdev = file->private_data; + struct ivpu_fw_info *fw = vdev->fw; + u32 trace_level; + int ret; + + ret = kstrtou32_from_user(user_buf, size, 0, &trace_level); + if (ret < 0) + return ret; + + fw->trace_level = trace_level; + + ivpu_jsm_trace_set_config(vdev, trace_level, fw->trace_destination_mask, + fw->trace_hw_component_mask); + + return size; +} + +static const struct file_operations fw_trace_level_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = fw_trace_level_fops_write, +}; + +static ssize_t +ivpu_reset_engine_fn(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) +{ + struct ivpu_device *vdev = file->private_data; + + if (!size) + return -EINVAL; + + if (ivpu_jsm_reset_engine(vdev, DRM_IVPU_ENGINE_COMPUTE)) + return -ENODEV; + if (ivpu_jsm_reset_engine(vdev, DRM_IVPU_ENGINE_COPY)) + return -ENODEV; + + return size; +} + +static ssize_t +ivpu_force_recovery_fn(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) +{ + struct ivpu_device *vdev = file->private_data; + + if (!size) + return -EINVAL; + + ivpu_pm_schedule_recovery(vdev); + return size; +} + +static const struct file_operations ivpu_force_recovery_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = ivpu_force_recovery_fn, +}; + +static const struct file_operations ivpu_reset_engine_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = ivpu_reset_engine_fn, +}; + +void ivpu_debugfs_init(struct drm_minor *minor) +{ + struct ivpu_device *vdev = to_ivpu_device(minor->dev); + + drm_debugfs_create_files(vdev_debugfs_list, ARRAY_SIZE(vdev_debugfs_list), + minor->debugfs_root, minor); + + debugfs_create_file("force_recovery", 0200, minor->debugfs_root, vdev, + &ivpu_force_recovery_fops); + + debugfs_create_file("fw_log", 0644, minor->debugfs_root, vdev, + &fw_log_fops); + debugfs_create_file("fw_trace_destination_mask", 0200, minor->debugfs_root, vdev, + &fw_trace_destination_mask_fops); + debugfs_create_file("fw_trace_hw_comp_mask", 0200, minor->debugfs_root, vdev, + &fw_trace_hw_comp_mask_fops); + debugfs_create_file("fw_trace_level", 0200, minor->debugfs_root, vdev, + &fw_trace_level_fops); + + debugfs_create_file("reset_engine", 0200, minor->debugfs_root, vdev, + &ivpu_reset_engine_fops); +} diff --git a/drivers/accel/ivpu/ivpu_debugfs.h b/drivers/accel/ivpu/ivpu_debugfs.h new file mode 100644 index 000000000000..78f80c1e00e4 --- /dev/null +++ b/drivers/accel/ivpu/ivpu_debugfs.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020-2023 Intel Corporation + */ + +#ifndef __IVPU_DEBUGFS_H__ +#define __IVPU_DEBUGFS_H__ + +struct drm_minor; + +void ivpu_debugfs_init(struct drm_minor *minor); + +#endif /* __IVPU_DEBUGFS_H__ */ diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 8396db2b5203..815dd9b588e9 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -14,6 +14,7 @@ #include <drm/drm_prime.h> #include "vpu_boot_api.h" +#include "ivpu_debugfs.h" #include "ivpu_drv.h" #include "ivpu_fw.h" #include "ivpu_gem.h" @@ -50,6 +51,10 @@ u8 ivpu_pll_max_ratio = U8_MAX; module_param_named(pll_max_ratio, ivpu_pll_max_ratio, byte, 0644); MODULE_PARM_DESC(pll_max_ratio, "Maximum PLL ratio used to set VPU frequency"); +bool ivpu_disable_mmu_cont_pages; +module_param_named(disable_mmu_cont_pages, ivpu_disable_mmu_cont_pages, bool, 0644); +MODULE_PARM_DESC(disable_mmu_cont_pages, "Disable MMU contiguous pages optimization"); + struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv) { struct ivpu_device *vdev = file_priv->vdev; @@ -369,10 +374,11 @@ static const struct drm_driver driver = { .open = ivpu_open, .postclose = ivpu_postclose, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = ivpu_gem_prime_import, - .gem_prime_mmap = drm_gem_prime_mmap, + +#if defined(CONFIG_DEBUG_FS) + .debugfs_init = ivpu_debugfs_init, +#endif .ioctls = ivpu_drm_ioctls, .num_ioctls = ARRAY_SIZE(ivpu_drm_ioctls), @@ -427,7 +433,7 @@ static int ivpu_pci_init(struct ivpu_device *vdev) return PTR_ERR(vdev->regb); } - ret = dma_set_mask_and_coherent(vdev->drm.dev, DMA_BIT_MASK(38)); + ret = dma_set_mask_and_coherent(vdev->drm.dev, DMA_BIT_MASK(vdev->hw->dma_bits)); if (ret) { ivpu_err(vdev, "Failed to set DMA mask: %d\n", ret); return ret; @@ -477,6 +483,8 @@ static int ivpu_dev_init(struct ivpu_device *vdev) return -ENOMEM; vdev->hw->ops = &ivpu_hw_mtl_ops; + vdev->hw->dma_bits = 38; + vdev->platform = IVPU_PLATFORM_INVALID; vdev->context_xa_limit.min = IVPU_USER_CONTEXT_MIN_SSID; vdev->context_xa_limit.max = IVPU_USER_CONTEXT_MAX_SSID; diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 399dc5dcefd7..8fe8cda2e39d 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -132,6 +132,7 @@ struct ivpu_file_priv { extern int ivpu_dbg_mask; extern u8 ivpu_pll_min_ratio; extern u8 ivpu_pll_max_ratio; +extern bool ivpu_disable_mmu_cont_pages; #define IVPU_TEST_MODE_DISABLED 0 #define IVPU_TEST_MODE_FW_TEST 1 diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index f58951a0d81b..317716482a15 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -11,6 +11,7 @@ #include "vpu_boot_api.h" #include "ivpu_drv.h" #include "ivpu_fw.h" +#include "ivpu_fw_log.h" #include "ivpu_gem.h" #include "ivpu_hw.h" #include "ivpu_ipc.h" @@ -51,13 +52,19 @@ static int ivpu_fw_request(struct ivpu_device *vdev) int ret = -ENOENT; int i; - if (ivpu_firmware) - return request_firmware(&vdev->fw->file, ivpu_firmware, vdev->drm.dev); + if (ivpu_firmware) { + ret = request_firmware(&vdev->fw->file, ivpu_firmware, vdev->drm.dev); + if (!ret) + vdev->fw->name = ivpu_firmware; + return ret; + } for (i = 0; i < ARRAY_SIZE(fw_names); i++) { ret = firmware_request_nowarn(&vdev->fw->file, fw_names[i], vdev->drm.dev); - if (!ret) + if (!ret) { + vdev->fw->name = fw_names[i]; return 0; + } } ivpu_err(vdev, "Failed to request firmware: %d\n", ret); @@ -142,7 +149,9 @@ static int ivpu_fw_parse(struct ivpu_device *vdev) } ivpu_dbg(vdev, FW_BOOT, "Header version: 0x%x, format 0x%x\n", fw_hdr->header_version, fw_hdr->image_format); - ivpu_dbg(vdev, FW_BOOT, "FW version: %s\n", (char *)fw_hdr + VPU_FW_HEADER_SIZE); + + ivpu_info(vdev, "Firmware: %s, version: %s", fw->name, + (const char *)fw_hdr + VPU_FW_HEADER_SIZE); if (IVPU_FW_CHECK_API(vdev, fw_hdr, BOOT, 3)) return -EINVAL; @@ -158,6 +167,10 @@ static int ivpu_fw_parse(struct ivpu_device *vdev) fw->cold_boot_entry_point = fw_hdr->entry_point; fw->entry_point = fw->cold_boot_entry_point; + fw->trace_level = min_t(u32, ivpu_log_level, IVPU_FW_LOG_FATAL); + fw->trace_destination_mask = VPU_TRACE_DESTINATION_VERBOSE_TRACING; + fw->trace_hw_component_mask = -1; + ivpu_dbg(vdev, FW_BOOT, "Size: file %lu image %u runtime %u shavenn %u\n", fw->file->size, fw->image_size, fw->runtime_size, fw->shave_nn_size); ivpu_dbg(vdev, FW_BOOT, "Address: runtime 0x%llx, load 0x%llx, entry point 0x%llx\n", @@ -189,6 +202,7 @@ static int ivpu_fw_update_global_range(struct ivpu_device *vdev) static int ivpu_fw_mem_init(struct ivpu_device *vdev) { struct ivpu_fw_info *fw = vdev->fw; + int log_verb_size; int ret; ret = ivpu_fw_update_global_range(vdev); @@ -201,17 +215,45 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev) return -ENOMEM; } + fw->mem_log_crit = ivpu_bo_alloc_internal(vdev, 0, IVPU_FW_CRITICAL_BUFFER_SIZE, + DRM_IVPU_BO_CACHED); + if (!fw->mem_log_crit) { + ivpu_err(vdev, "Failed to allocate critical log buffer\n"); + ret = -ENOMEM; + goto err_free_fw_mem; + } + + if (ivpu_log_level <= IVPU_FW_LOG_INFO) + log_verb_size = IVPU_FW_VERBOSE_BUFFER_LARGE_SIZE; + else + log_verb_size = IVPU_FW_VERBOSE_BUFFER_SMALL_SIZE; + + fw->mem_log_verb = ivpu_bo_alloc_internal(vdev, 0, log_verb_size, DRM_IVPU_BO_CACHED); + if (!fw->mem_log_verb) { + ivpu_err(vdev, "Failed to allocate verbose log buffer\n"); + ret = -ENOMEM; + goto err_free_log_crit; + } + if (fw->shave_nn_size) { fw->mem_shave_nn = ivpu_bo_alloc_internal(vdev, vdev->hw->ranges.global_high.start, fw->shave_nn_size, DRM_IVPU_BO_UNCACHED); if (!fw->mem_shave_nn) { ivpu_err(vdev, "Failed to allocate shavenn buffer\n"); - ivpu_bo_free_internal(fw->mem); - return -ENOMEM; + ret = -ENOMEM; + goto err_free_log_verb; } } return 0; + +err_free_log_verb: + ivpu_bo_free_internal(fw->mem_log_verb); +err_free_log_crit: + ivpu_bo_free_internal(fw->mem_log_crit); +err_free_fw_mem: + ivpu_bo_free_internal(fw->mem); + return ret; } static void ivpu_fw_mem_fini(struct ivpu_device *vdev) @@ -223,7 +265,12 @@ static void ivpu_fw_mem_fini(struct ivpu_device *vdev) fw->mem_shave_nn = NULL; } + ivpu_bo_free_internal(fw->mem_log_verb); + ivpu_bo_free_internal(fw->mem_log_crit); ivpu_bo_free_internal(fw->mem); + + fw->mem_log_verb = NULL; + fw->mem_log_crit = NULL; fw->mem = NULL; } @@ -424,6 +471,15 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params boot_params->pn_freq_pll_ratio = vdev->hw->pll.pn_ratio; boot_params->max_freq_pll_ratio = vdev->hw->pll.max_ratio; + boot_params->default_trace_level = vdev->fw->trace_level; + boot_params->tracing_buff_message_format_mask = BIT(VPU_TRACING_FORMAT_STRING); + boot_params->trace_destination_mask = vdev->fw->trace_destination_mask; + boot_params->trace_hw_component_mask = vdev->fw->trace_hw_component_mask; + boot_params->crit_tracing_buff_addr = vdev->fw->mem_log_crit->vpu_addr; + boot_params->crit_tracing_buff_size = vdev->fw->mem_log_crit->base.size; + boot_params->verbose_tracing_buff_addr = vdev->fw->mem_log_verb->vpu_addr; + boot_params->verbose_tracing_buff_size = vdev->fw->mem_log_verb->base.size; + boot_params->punit_telemetry_sram_base = ivpu_hw_reg_telemetry_offset_get(vdev); boot_params->punit_telemetry_sram_size = ivpu_hw_reg_telemetry_size_get(vdev); boot_params->vpu_telemetry_enable = ivpu_hw_reg_telemetry_enable_get(vdev); diff --git a/drivers/accel/ivpu/ivpu_fw.h b/drivers/accel/ivpu/ivpu_fw.h index 8d275c802d1c..8567fdf925fe 100644 --- a/drivers/accel/ivpu/ivpu_fw.h +++ b/drivers/accel/ivpu/ivpu_fw.h @@ -12,6 +12,7 @@ struct vpu_boot_params; struct ivpu_fw_info { const struct firmware *file; + const char *name; struct ivpu_bo *mem; struct ivpu_bo *mem_shave_nn; struct ivpu_bo *mem_log_crit; @@ -23,6 +24,9 @@ struct ivpu_fw_info { u32 shave_nn_size; u64 entry_point; /* Cold or warm boot entry point for next boot */ u64 cold_boot_entry_point; + u32 trace_level; + u32 trace_destination_mask; + u64 trace_hw_component_mask; }; int ivpu_fw_init(struct ivpu_device *vdev); diff --git a/drivers/accel/ivpu/ivpu_fw_log.c b/drivers/accel/ivpu/ivpu_fw_log.c new file mode 100644 index 000000000000..95065cac9fbd --- /dev/null +++ b/drivers/accel/ivpu/ivpu_fw_log.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2023 Intel Corporation + */ + +#include <linux/ctype.h> +#include <linux/highmem.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> + +#include "vpu_boot_api.h" +#include "ivpu_drv.h" +#include "ivpu_fw.h" +#include "ivpu_fw_log.h" +#include "ivpu_gem.h" + +#define IVPU_FW_LOG_LINE_LENGTH 256 + +unsigned int ivpu_log_level = IVPU_FW_LOG_ERROR; +module_param(ivpu_log_level, uint, 0444); +MODULE_PARM_DESC(ivpu_log_level, + "VPU firmware default trace level: debug=" __stringify(IVPU_FW_LOG_DEBUG) + " info=" __stringify(IVPU_FW_LOG_INFO) + " warn=" __stringify(IVPU_FW_LOG_WARN) + " error=" __stringify(IVPU_FW_LOG_ERROR) + " fatal=" __stringify(IVPU_FW_LOG_FATAL)); + +static int fw_log_ptr(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *offset, + struct vpu_tracing_buffer_header **log_header) +{ + struct vpu_tracing_buffer_header *log; + + if ((*offset + sizeof(*log)) > bo->base.size) + return -EINVAL; + + log = bo->kvaddr + *offset; + + if (log->vpu_canary_start != VPU_TRACING_BUFFER_CANARY) + return -EINVAL; + + if (log->header_size < sizeof(*log) || log->header_size > 1024) { + ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size); + return -EINVAL; + } + if ((char *)log + log->size > (char *)bo->kvaddr + bo->base.size) { + ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size); + return -EINVAL; + } + + *log_header = log; + *offset += log->size; + + ivpu_dbg(vdev, FW_BOOT, + "FW log name \"%s\", write offset 0x%x size 0x%x, wrap count %d, hdr version %d size %d format %d, alignment %d", + log->name, log->write_index, log->size, log->wrap_count, log->header_version, + log->header_size, log->format, log->alignment); + + return 0; +} + +static void buffer_print(char *buffer, u32 size, struct drm_printer *p) +{ + char line[IVPU_FW_LOG_LINE_LENGTH]; + u32 index = 0; + + if (!size || !buffer) + return; + + while (size--) { + if (*buffer == '\n' || *buffer == 0) { + line[index] = 0; + if (index != 0) + drm_printf(p, "%s\n", line); + index = 0; + buffer++; + continue; + } + if (index == IVPU_FW_LOG_LINE_LENGTH - 1) { + line[index] = 0; + index = 0; + drm_printf(p, "%s\n", line); + } + if (*buffer != '\r' && (isprint(*buffer) || iscntrl(*buffer))) + line[index++] = *buffer; + buffer++; + } + line[index] = 0; + if (index != 0) + drm_printf(p, "%s\n", line); +} + +static void fw_log_print_buffer(struct ivpu_device *vdev, struct vpu_tracing_buffer_header *log, + const char *prefix, bool only_new_msgs, struct drm_printer *p) +{ + char *log_buffer = (void *)log + log->header_size; + u32 log_size = log->size - log->header_size; + u32 log_start = log->read_index; + u32 log_end = log->write_index; + + if (!(log->write_index || log->wrap_count) || + (log->write_index == log->read_index && only_new_msgs)) { + drm_printf(p, "==== %s \"%s\" log empty ====\n", prefix, log->name); + return; + } + + drm_printf(p, "==== %s \"%s\" log start ====\n", prefix, log->name); + if (log->write_index > log->read_index) { + buffer_print(log_buffer + log_start, log_end - log_start, p); + } else { + buffer_print(log_buffer + log_end, log_size - log_end, p); + buffer_print(log_buffer, log_end, p); + } + drm_printf(p, "\x1b[0m"); + drm_printf(p, "==== %s \"%s\" log end ====\n", prefix, log->name); +} + +void ivpu_fw_log_print(struct ivpu_device *vdev, bool only_new_msgs, struct drm_printer *p) +{ + struct vpu_tracing_buffer_header *log_header; + u32 next = 0; + + while (fw_log_ptr(vdev, vdev->fw->mem_log_crit, &next, &log_header) == 0) + fw_log_print_buffer(vdev, log_header, "VPU critical", only_new_msgs, p); + + next = 0; + while (fw_log_ptr(vdev, vdev->fw->mem_log_verb, &next, &log_header) == 0) + fw_log_print_buffer(vdev, log_header, "VPU verbose", only_new_msgs, p); +} + +void ivpu_fw_log_clear(struct ivpu_device *vdev) +{ + struct vpu_tracing_buffer_header *log_header; + u32 next = 0; + + while (fw_log_ptr(vdev, vdev->fw->mem_log_crit, &next, &log_header) == 0) + log_header->read_index = log_header->write_index; + + next = 0; + while (fw_log_ptr(vdev, vdev->fw->mem_log_verb, &next, &log_header) == 0) + log_header->read_index = log_header->write_index; +} diff --git a/drivers/accel/ivpu/ivpu_fw_log.h b/drivers/accel/ivpu/ivpu_fw_log.h new file mode 100644 index 000000000000..0b2573f6f315 --- /dev/null +++ b/drivers/accel/ivpu/ivpu_fw_log.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020-2023 Intel Corporation + */ + +#ifndef __IVPU_FW_LOG_H__ +#define __IVPU_FW_LOG_H__ + +#include <linux/types.h> + +#include <drm/drm_print.h> + +#include "ivpu_drv.h" + +#define IVPU_FW_LOG_DEFAULT 0 +#define IVPU_FW_LOG_DEBUG 1 +#define IVPU_FW_LOG_INFO 2 +#define IVPU_FW_LOG_WARN 3 +#define IVPU_FW_LOG_ERROR 4 +#define IVPU_FW_LOG_FATAL 5 + +extern unsigned int ivpu_log_level; + +#define IVPU_FW_VERBOSE_BUFFER_SMALL_SIZE SZ_1M +#define IVPU_FW_VERBOSE_BUFFER_LARGE_SIZE SZ_8M +#define IVPU_FW_CRITICAL_BUFFER_SIZE SZ_512K + +void ivpu_fw_log_print(struct ivpu_device *vdev, bool only_new_msgs, struct drm_printer *p); +void ivpu_fw_log_clear(struct ivpu_device *vdev); + +static inline void ivpu_fw_log_dump(struct ivpu_device *vdev) +{ + struct drm_printer p = drm_info_printer(vdev->drm.dev); + + ivpu_fw_log_print(vdev, false, &p); +} + +#endif /* __IVPU_FW_LOG_H__ */ diff --git a/drivers/accel/ivpu/ivpu_hw.h b/drivers/accel/ivpu/ivpu_hw.h index 50a9304ab09c..0393901be492 100644 --- a/drivers/accel/ivpu/ivpu_hw.h +++ b/drivers/accel/ivpu/ivpu_hw.h @@ -57,6 +57,7 @@ struct ivpu_hw_info { u32 tile_fuse; u32 sku; u16 config; + int dma_bits; }; extern const struct ivpu_hw_ops ivpu_hw_mtl_ops; diff --git a/drivers/accel/ivpu/ivpu_hw_mtl.c b/drivers/accel/ivpu/ivpu_hw_mtl.c index 2a5dd3a5dc46..f1211e74017d 100644 --- a/drivers/accel/ivpu/ivpu_hw_mtl.c +++ b/drivers/accel/ivpu/ivpu_hw_mtl.c @@ -551,21 +551,10 @@ static void ivpu_boot_tbu_mmu_enable(struct ivpu_device *vdev) { u32 val = REGV_RD32(MTL_VPU_HOST_IF_TBU_MMUSSIDV); - if (ivpu_is_fpga(vdev)) { - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU0_AWMMUSSIDV, val); - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU0_ARMMUSSIDV, val); - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU2_AWMMUSSIDV, val); - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU2_ARMMUSSIDV, val); - } else { - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU0_AWMMUSSIDV, val); - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU0_ARMMUSSIDV, val); - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU1_AWMMUSSIDV, val); - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU1_ARMMUSSIDV, val); - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU2_AWMMUSSIDV, val); - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU2_ARMMUSSIDV, val); - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU3_AWMMUSSIDV, val); - val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU3_ARMMUSSIDV, val); - } + val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU0_AWMMUSSIDV, val); + val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU0_ARMMUSSIDV, val); + val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU2_AWMMUSSIDV, val); + val = REG_SET_FLD(MTL_VPU_HOST_IF_TBU_MMUSSIDV, TBU2_ARMMUSSIDV, val); REGV_WR32(MTL_VPU_HOST_IF_TBU_MMUSSIDV, val); } diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index d45be0615b47..de9e69f70af7 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -289,15 +289,13 @@ ivpu_create_job(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count) { struct ivpu_device *vdev = file_priv->vdev; struct ivpu_job *job; - size_t buf_size; int ret; ret = ivpu_rpm_get(vdev); if (ret < 0) return NULL; - buf_size = sizeof(*job) + bo_count * sizeof(struct ivpu_bo *); - job = kzalloc(buf_size, GFP_KERNEL); + job = kzalloc(struct_size(job, bos, bo_count), GFP_KERNEL); if (!job) goto err_rpm_put; diff --git a/drivers/accel/ivpu/ivpu_mmu.c b/drivers/accel/ivpu/ivpu_mmu.c index b8b259b3aa63..53878e77aad3 100644 --- a/drivers/accel/ivpu/ivpu_mmu.c +++ b/drivers/accel/ivpu/ivpu_mmu.c @@ -143,6 +143,16 @@ #define IVPU_MMU_CD_0_ASET BIT(47) #define IVPU_MMU_CD_0_ASID GENMASK_ULL(63, 48) +#define IVPU_MMU_T0SZ_48BIT 16 +#define IVPU_MMU_T0SZ_38BIT 26 + +#define IVPU_MMU_IPS_48BIT 5 +#define IVPU_MMU_IPS_44BIT 4 +#define IVPU_MMU_IPS_42BIT 3 +#define IVPU_MMU_IPS_40BIT 2 +#define IVPU_MMU_IPS_36BIT 1 +#define IVPU_MMU_IPS_32BIT 0 + #define IVPU_MMU_CD_1_TTB0_MASK GENMASK_ULL(51, 4) #define IVPU_MMU_STE_0_S1CDMAX GENMASK_ULL(63, 59) @@ -617,12 +627,12 @@ static int ivpu_mmu_cd_add(struct ivpu_device *vdev, u32 ssid, u64 cd_dma) entry = cdtab->base + (ssid * IVPU_MMU_CDTAB_ENT_SIZE); if (cd_dma != 0) { - cd[0] = FIELD_PREP(IVPU_MMU_CD_0_TCR_T0SZ, 26) | + cd[0] = FIELD_PREP(IVPU_MMU_CD_0_TCR_T0SZ, IVPU_MMU_T0SZ_48BIT) | FIELD_PREP(IVPU_MMU_CD_0_TCR_TG0, 0) | FIELD_PREP(IVPU_MMU_CD_0_TCR_IRGN0, 0) | FIELD_PREP(IVPU_MMU_CD_0_TCR_ORGN0, 0) | FIELD_PREP(IVPU_MMU_CD_0_TCR_SH0, 0) | - FIELD_PREP(IVPU_MMU_CD_0_TCR_IPS, 3) | + FIELD_PREP(IVPU_MMU_CD_0_TCR_IPS, IVPU_MMU_IPS_48BIT) | FIELD_PREP(IVPU_MMU_CD_0_ASID, ssid) | IVPU_MMU_CD_0_TCR_EPD1 | IVPU_MMU_CD_0_AA64 | diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c index 8ce9b12ac356..465a82298476 100644 --- a/drivers/accel/ivpu/ivpu_mmu_context.c +++ b/drivers/accel/ivpu/ivpu_mmu_context.c @@ -11,10 +11,12 @@ #include "ivpu_mmu.h" #include "ivpu_mmu_context.h" -#define IVPU_MMU_PGD_INDEX_MASK GENMASK(38, 30) +#define IVPU_MMU_PGD_INDEX_MASK GENMASK(47, 39) +#define IVPU_MMU_PUD_INDEX_MASK GENMASK(38, 30) #define IVPU_MMU_PMD_INDEX_MASK GENMASK(29, 21) #define IVPU_MMU_PTE_INDEX_MASK GENMASK(20, 12) -#define IVPU_MMU_ENTRY_FLAGS_MASK GENMASK(11, 0) +#define IVPU_MMU_ENTRY_FLAGS_MASK (BIT(52) | GENMASK(11, 0)) +#define IVPU_MMU_ENTRY_FLAG_CONT BIT(52) #define IVPU_MMU_ENTRY_FLAG_NG BIT(11) #define IVPU_MMU_ENTRY_FLAG_AF BIT(10) #define IVPU_MMU_ENTRY_FLAG_USER BIT(6) @@ -22,10 +24,13 @@ #define IVPU_MMU_ENTRY_FLAG_TYPE_PAGE BIT(1) #define IVPU_MMU_ENTRY_FLAG_VALID BIT(0) -#define IVPU_MMU_PAGE_SIZE SZ_4K -#define IVPU_MMU_PTE_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PAGE_SIZE) -#define IVPU_MMU_PMD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PTE_MAP_SIZE) -#define IVPU_MMU_PGTABLE_SIZE (IVPU_MMU_PGTABLE_ENTRIES * sizeof(u64)) +#define IVPU_MMU_PAGE_SIZE SZ_4K +#define IVPU_MMU_CONT_PAGES_SIZE (IVPU_MMU_PAGE_SIZE * 16) +#define IVPU_MMU_PTE_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PAGE_SIZE) +#define IVPU_MMU_PMD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PTE_MAP_SIZE) +#define IVPU_MMU_PUD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PMD_MAP_SIZE) +#define IVPU_MMU_PGD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PUD_MAP_SIZE) +#define IVPU_MMU_PGTABLE_SIZE (IVPU_MMU_PGTABLE_ENTRIES * sizeof(u64)) #define IVPU_MMU_DUMMY_ADDRESS 0xdeadb000 #define IVPU_MMU_ENTRY_VALID (IVPU_MMU_ENTRY_FLAG_TYPE_PAGE | IVPU_MMU_ENTRY_FLAG_VALID) @@ -36,167 +41,268 @@ static int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) { dma_addr_t pgd_dma; - u64 *pgd; - pgd = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pgd_dma, GFP_KERNEL); - if (!pgd) + pgtable->pgd_dma_ptr = dma_alloc_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pgd_dma, + GFP_KERNEL); + if (!pgtable->pgd_dma_ptr) return -ENOMEM; - pgtable->pgd = pgd; pgtable->pgd_dma = pgd_dma; return 0; } -static void ivpu_mmu_pgtable_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) +static void ivpu_mmu_pgtable_free(struct ivpu_device *vdev, u64 *cpu_addr, dma_addr_t dma_addr) { - int pgd_index, pmd_index; + if (cpu_addr) + dma_free_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, cpu_addr, + dma_addr & ~IVPU_MMU_ENTRY_FLAGS_MASK); +} + +static void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) +{ + int pgd_idx, pud_idx, pmd_idx; + dma_addr_t pud_dma, pmd_dma, pte_dma; + u64 *pud_dma_ptr, *pmd_dma_ptr, *pte_dma_ptr; - for (pgd_index = 0; pgd_index < IVPU_MMU_PGTABLE_ENTRIES; ++pgd_index) { - u64 **pmd_entries = pgtable->pgd_cpu_entries[pgd_index]; - u64 *pmd = pgtable->pgd_entries[pgd_index]; + for (pgd_idx = 0; pgd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pgd_idx) { + pud_dma_ptr = pgtable->pud_ptrs[pgd_idx]; + pud_dma = pgtable->pgd_dma_ptr[pgd_idx]; - if (!pmd_entries) + if (!pud_dma_ptr) continue; - for (pmd_index = 0; pmd_index < IVPU_MMU_PGTABLE_ENTRIES; ++pmd_index) { - if (pmd_entries[pmd_index]) - dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, - pmd_entries[pmd_index], - pmd[pmd_index] & ~IVPU_MMU_ENTRY_FLAGS_MASK); + for (pud_idx = 0; pud_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pud_idx) { + pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx]; + pmd_dma = pgtable->pud_ptrs[pgd_idx][pud_idx]; + + if (!pmd_dma_ptr) + continue; + + for (pmd_idx = 0; pmd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pmd_idx) { + pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx]; + pte_dma = pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx]; + + ivpu_mmu_pgtable_free(vdev, pte_dma_ptr, pte_dma); + } + + kfree(pgtable->pte_ptrs[pgd_idx][pud_idx]); + ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma); } - kfree(pmd_entries); - dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, pgtable->pgd_entries[pgd_index], - pgtable->pgd[pgd_index] & ~IVPU_MMU_ENTRY_FLAGS_MASK); + kfree(pgtable->pmd_ptrs[pgd_idx]); + kfree(pgtable->pte_ptrs[pgd_idx]); + ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma); } - dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, pgtable->pgd, - pgtable->pgd_dma & ~IVPU_MMU_ENTRY_FLAGS_MASK); + ivpu_mmu_pgtable_free(vdev, pgtable->pgd_dma_ptr, pgtable->pgd_dma); +} + +static u64* +ivpu_mmu_ensure_pud(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx) +{ + u64 *pud_dma_ptr = pgtable->pud_ptrs[pgd_idx]; + dma_addr_t pud_dma; + + if (pud_dma_ptr) + return pud_dma_ptr; + + pud_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pud_dma, GFP_KERNEL); + if (!pud_dma_ptr) + return NULL; + + drm_WARN_ON(&vdev->drm, pgtable->pmd_ptrs[pgd_idx]); + pgtable->pmd_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); + if (!pgtable->pmd_ptrs[pgd_idx]) + goto err_free_pud_dma_ptr; + + drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx]); + pgtable->pte_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); + if (!pgtable->pte_ptrs[pgd_idx]) + goto err_free_pmd_ptrs; + + pgtable->pud_ptrs[pgd_idx] = pud_dma_ptr; + pgtable->pgd_dma_ptr[pgd_idx] = pud_dma | IVPU_MMU_ENTRY_VALID; + + return pud_dma_ptr; + +err_free_pmd_ptrs: + kfree(pgtable->pmd_ptrs[pgd_idx]); + +err_free_pud_dma_ptr: + ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma); + return NULL; } static u64* -ivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, u64 pgd_index) +ivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx, + int pud_idx) { - u64 **pmd_entries; + u64 *pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx]; dma_addr_t pmd_dma; - u64 *pmd; - if (pgtable->pgd_entries[pgd_index]) - return pgtable->pgd_entries[pgd_index]; + if (pmd_dma_ptr) + return pmd_dma_ptr; - pmd = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pmd_dma, GFP_KERNEL); - if (!pmd) + pmd_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pmd_dma, GFP_KERNEL); + if (!pmd_dma_ptr) return NULL; - pmd_entries = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); - if (!pmd_entries) - goto err_free_pgd; + drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx][pud_idx]); + pgtable->pte_ptrs[pgd_idx][pud_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); + if (!pgtable->pte_ptrs[pgd_idx][pud_idx]) + goto err_free_pmd_dma_ptr; - pgtable->pgd_entries[pgd_index] = pmd; - pgtable->pgd_cpu_entries[pgd_index] = pmd_entries; - pgtable->pgd[pgd_index] = pmd_dma | IVPU_MMU_ENTRY_VALID; + pgtable->pmd_ptrs[pgd_idx][pud_idx] = pmd_dma_ptr; + pgtable->pud_ptrs[pgd_idx][pud_idx] = pmd_dma | IVPU_MMU_ENTRY_VALID; - return pmd; + return pmd_dma_ptr; -err_free_pgd: - dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, pmd, pmd_dma); +err_free_pmd_dma_ptr: + ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma); return NULL; } static u64* ivpu_mmu_ensure_pte(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, - int pgd_index, int pmd_index) + int pgd_idx, int pud_idx, int pmd_idx) { + u64 *pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx]; dma_addr_t pte_dma; - u64 *pte; - if (pgtable->pgd_cpu_entries[pgd_index][pmd_index]) - return pgtable->pgd_cpu_entries[pgd_index][pmd_index]; + if (pte_dma_ptr) + return pte_dma_ptr; - pte = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pte_dma, GFP_KERNEL); - if (!pte) + pte_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pte_dma, GFP_KERNEL); + if (!pte_dma_ptr) return NULL; - pgtable->pgd_cpu_entries[pgd_index][pmd_index] = pte; - pgtable->pgd_entries[pgd_index][pmd_index] = pte_dma | IVPU_MMU_ENTRY_VALID; + pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma_ptr; + pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma | IVPU_MMU_ENTRY_VALID; - return pte; + return pte_dma_ptr; } static int ivpu_mmu_context_map_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, - u64 vpu_addr, dma_addr_t dma_addr, int prot) + u64 vpu_addr, dma_addr_t dma_addr, u64 prot) { u64 *pte; - int pgd_index = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); - int pmd_index = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); - int pte_index = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); + int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); + int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); + int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); + int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); + + /* Allocate PUD - second level page table if needed */ + if (!ivpu_mmu_ensure_pud(vdev, &ctx->pgtable, pgd_idx)) + return -ENOMEM; - /* Allocate PMD - second level page table if needed */ - if (!ivpu_mmu_ensure_pmd(vdev, &ctx->pgtable, pgd_index)) + /* Allocate PMD - third level page table if needed */ + if (!ivpu_mmu_ensure_pmd(vdev, &ctx->pgtable, pgd_idx, pud_idx)) return -ENOMEM; - /* Allocate PTE - third level page table if needed */ - pte = ivpu_mmu_ensure_pte(vdev, &ctx->pgtable, pgd_index, pmd_index); + /* Allocate PTE - fourth level page table if needed */ + pte = ivpu_mmu_ensure_pte(vdev, &ctx->pgtable, pgd_idx, pud_idx, pmd_idx); if (!pte) return -ENOMEM; - /* Update PTE - third level page table with DMA address */ - pte[pte_index] = dma_addr | prot; + /* Update PTE */ + pte[pte_idx] = dma_addr | prot; + + return 0; +} + +static int +ivpu_mmu_context_map_cont_64k(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr, + dma_addr_t dma_addr, u64 prot) +{ + size_t size = IVPU_MMU_CONT_PAGES_SIZE; + + drm_WARN_ON(&vdev->drm, !IS_ALIGNED(vpu_addr, size)); + drm_WARN_ON(&vdev->drm, !IS_ALIGNED(dma_addr, size)); + + prot |= IVPU_MMU_ENTRY_FLAG_CONT; + + while (size) { + int ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot); + + if (ret) + return ret; + + size -= IVPU_MMU_PAGE_SIZE; + vpu_addr += IVPU_MMU_PAGE_SIZE; + dma_addr += IVPU_MMU_PAGE_SIZE; + } return 0; } static void ivpu_mmu_context_unmap_page(struct ivpu_mmu_context *ctx, u64 vpu_addr) { - int pgd_index = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); - int pmd_index = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); - int pte_index = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); + int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); + int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); + int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); + int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); /* Update PTE with dummy physical address and clear flags */ - ctx->pgtable.pgd_cpu_entries[pgd_index][pmd_index][pte_index] = IVPU_MMU_ENTRY_INVALID; + ctx->pgtable.pte_ptrs[pgd_idx][pud_idx][pmd_idx][pte_idx] = IVPU_MMU_ENTRY_INVALID; } static void ivpu_mmu_context_flush_page_tables(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size) { + struct ivpu_mmu_pgtable *pgtable = &ctx->pgtable; u64 end_addr = vpu_addr + size; - u64 *pgd = ctx->pgtable.pgd; /* Align to PMD entry (2 MB) */ vpu_addr &= ~(IVPU_MMU_PTE_MAP_SIZE - 1); while (vpu_addr < end_addr) { - int pgd_index = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); - u64 pmd_end = (pgd_index + 1) * (u64)IVPU_MMU_PMD_MAP_SIZE; - u64 *pmd = ctx->pgtable.pgd_entries[pgd_index]; - - while (vpu_addr < end_addr && vpu_addr < pmd_end) { - int pmd_index = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); - u64 *pte = ctx->pgtable.pgd_cpu_entries[pgd_index][pmd_index]; - - clflush_cache_range(pte, IVPU_MMU_PGTABLE_SIZE); - vpu_addr += IVPU_MMU_PTE_MAP_SIZE; + int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); + u64 pud_end = (pgd_idx + 1) * (u64)IVPU_MMU_PUD_MAP_SIZE; + + while (vpu_addr < end_addr && vpu_addr < pud_end) { + int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); + u64 pmd_end = (pud_idx + 1) * (u64)IVPU_MMU_PMD_MAP_SIZE; + + while (vpu_addr < end_addr && vpu_addr < pmd_end) { + int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); + + clflush_cache_range(pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx], + IVPU_MMU_PGTABLE_SIZE); + vpu_addr += IVPU_MMU_PTE_MAP_SIZE; + } + clflush_cache_range(pgtable->pmd_ptrs[pgd_idx][pud_idx], + IVPU_MMU_PGTABLE_SIZE); } - clflush_cache_range(pmd, IVPU_MMU_PGTABLE_SIZE); + clflush_cache_range(pgtable->pud_ptrs[pgd_idx], IVPU_MMU_PGTABLE_SIZE); } - clflush_cache_range(pgd, IVPU_MMU_PGTABLE_SIZE); + clflush_cache_range(pgtable->pgd_dma_ptr, IVPU_MMU_PGTABLE_SIZE); } static int ivpu_mmu_context_map_pages(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, - u64 vpu_addr, dma_addr_t dma_addr, size_t size, int prot) + u64 vpu_addr, dma_addr_t dma_addr, size_t size, u64 prot) { + int map_size; + int ret; + while (size) { - int ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot); + if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE && + IS_ALIGNED(vpu_addr | dma_addr, IVPU_MMU_CONT_PAGES_SIZE)) { + ret = ivpu_mmu_context_map_cont_64k(vdev, ctx, vpu_addr, dma_addr, prot); + map_size = IVPU_MMU_CONT_PAGES_SIZE; + } else { + ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot); + map_size = IVPU_MMU_PAGE_SIZE; + } if (ret) return ret; - vpu_addr += IVPU_MMU_PAGE_SIZE; - dma_addr += IVPU_MMU_PAGE_SIZE; - size -= IVPU_MMU_PAGE_SIZE; + vpu_addr += map_size; + dma_addr += map_size; + size -= map_size; } return 0; @@ -216,8 +322,8 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr, struct sg_table *sgt, bool llc_coherent) { struct scatterlist *sg; - int prot; int ret; + u64 prot; u64 i; if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) @@ -237,7 +343,7 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, mutex_lock(&ctx->lock); for_each_sgtable_dma_sg(sgt, sg, i) { - u64 dma_addr = sg_dma_address(sg) - sg->offset; + dma_addr_t dma_addr = sg_dma_address(sg) - sg->offset; size_t size = sg_dma_len(sg) + sg->offset; ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot); @@ -293,8 +399,14 @@ ivpu_mmu_context_insert_node_locked(struct ivpu_mmu_context *ctx, { lockdep_assert_held(&ctx->lock); - return drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE, - 0, range->start, range->end, DRM_MM_INSERT_BEST); + if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE) { + if (!drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_CONT_PAGES_SIZE, 0, + range->start, range->end, DRM_MM_INSERT_BEST)) + return 0; + } + + return drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE, 0, + range->start, range->end, DRM_MM_INSERT_BEST); } void @@ -334,11 +446,15 @@ ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u3 static void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) { - drm_WARN_ON(&vdev->drm, !ctx->pgtable.pgd); + if (drm_WARN_ON(&vdev->drm, !ctx->pgtable.pgd_dma_ptr)) + return; mutex_destroy(&ctx->lock); - ivpu_mmu_pgtable_free(vdev, &ctx->pgtable); + ivpu_mmu_pgtables_free(vdev, &ctx->pgtable); drm_mm_takedown(&ctx->mm); + + ctx->pgtable.pgd_dma_ptr = NULL; + ctx->pgtable.pgd_dma = 0; } int ivpu_mmu_global_context_init(struct ivpu_device *vdev) diff --git a/drivers/accel/ivpu/ivpu_mmu_context.h b/drivers/accel/ivpu/ivpu_mmu_context.h index ddf11b95023a..961a0d6a6c7f 100644 --- a/drivers/accel/ivpu/ivpu_mmu_context.h +++ b/drivers/accel/ivpu/ivpu_mmu_context.h @@ -12,12 +12,13 @@ struct ivpu_device; struct ivpu_file_priv; struct ivpu_addr_range; -#define IVPU_MMU_PGTABLE_ENTRIES 512 +#define IVPU_MMU_PGTABLE_ENTRIES 512ull struct ivpu_mmu_pgtable { - u64 **pgd_cpu_entries[IVPU_MMU_PGTABLE_ENTRIES]; - u64 *pgd_entries[IVPU_MMU_PGTABLE_ENTRIES]; - u64 *pgd; + u64 ***pte_ptrs[IVPU_MMU_PGTABLE_ENTRIES]; + u64 **pmd_ptrs[IVPU_MMU_PGTABLE_ENTRIES]; + u64 *pud_ptrs[IVPU_MMU_PGTABLE_ENTRIES]; + u64 *pgd_dma_ptr; dma_addr_t pgd_dma; }; diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c index aa4d56dc52b3..e6f27daf5560 100644 --- a/drivers/accel/ivpu/ivpu_pm.c +++ b/drivers/accel/ivpu/ivpu_pm.c @@ -259,6 +259,7 @@ void ivpu_pm_reset_prepare_cb(struct pci_dev *pdev) pm_runtime_get_sync(vdev->drm.dev); ivpu_dbg(vdev, PM, "Pre-reset..\n"); + atomic_inc(&vdev->pm->reset_counter); atomic_set(&vdev->pm->in_reset, 1); ivpu_shutdown(vdev); ivpu_pm_prepare_cold_boot(vdev); diff --git a/drivers/accel/ivpu/ivpu_pm.h b/drivers/accel/ivpu/ivpu_pm.h index baca98187255..fd4eada1290f 100644 --- a/drivers/accel/ivpu/ivpu_pm.h +++ b/drivers/accel/ivpu/ivpu_pm.h @@ -14,6 +14,7 @@ struct ivpu_pm_info { struct ivpu_device *vdev; struct work_struct recovery_work; atomic_t in_reset; + atomic_t reset_counter; bool is_warmboot; u32 suspend_reschedule_counter; }; diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c index b5ba550a0c04..b5de82e6eb4d 100644 --- a/drivers/accel/qaic/qaic_drv.c +++ b/drivers/accel/qaic/qaic_drv.c @@ -165,7 +165,6 @@ static const struct drm_driver qaic_accel_driver = { .ioctls = qaic_drm_ioctls, .num_ioctls = ARRAY_SIZE(qaic_drm_ioctls), - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = qaic_gem_prime_import, }; diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index aa4ea8530cb3..21916bba77d5 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -131,7 +131,6 @@ static struct file_system_type dma_buf_fs_type = { static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma) { struct dma_buf *dmabuf; - int ret; if (!is_dma_buf_file(file)) return -EINVAL; @@ -147,11 +146,7 @@ static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma) dmabuf->size >> PAGE_SHIFT) return -EINVAL; - dma_resv_lock(dmabuf->resv, NULL); - ret = dmabuf->ops->mmap(dmabuf, vma); - dma_resv_unlock(dmabuf->resv); - - return ret; + return dmabuf->ops->mmap(dmabuf, vma); } static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence) @@ -850,6 +845,7 @@ static struct sg_table * __map_dma_buf(struct dma_buf_attachment *attach, * - &dma_buf_ops.release() * - &dma_buf_ops.begin_cpu_access() * - &dma_buf_ops.end_cpu_access() + * - &dma_buf_ops.mmap() * * 2. These &dma_buf_ops callbacks are invoked with locked dma-buf * reservation and exporter can't take the lock: @@ -858,7 +854,6 @@ static struct sg_table * __map_dma_buf(struct dma_buf_attachment *attach, * - &dma_buf_ops.unpin() * - &dma_buf_ops.map_dma_buf() * - &dma_buf_ops.unmap_dma_buf() - * - &dma_buf_ops.mmap() * - &dma_buf_ops.vmap() * - &dma_buf_ops.vunmap() * @@ -1463,8 +1458,6 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_end_cpu_access, DMA_BUF); int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, unsigned long pgoff) { - int ret; - if (WARN_ON(!dmabuf || !vma)) return -EINVAL; @@ -1485,11 +1478,7 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, vma_set_file(vma, dmabuf->file); vma->vm_pgoff = pgoff; - dma_resv_lock(dmabuf->resv, NULL); - ret = dmabuf->ops->mmap(dmabuf, vma); - dma_resv_unlock(dmabuf->resv); - - return ret; + return dmabuf->ops->mmap(dmabuf, vma); } EXPORT_SYMBOL_NS_GPL(dma_buf_mmap, DMA_BUF); diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c index a7f048048864..ee899f8e6721 100644 --- a/drivers/dma-buf/heaps/cma_heap.c +++ b/drivers/dma-buf/heaps/cma_heap.c @@ -13,7 +13,6 @@ #include <linux/dma-buf.h> #include <linux/dma-heap.h> #include <linux/dma-map-ops.h> -#include <linux/dma-resv.h> #include <linux/err.h> #include <linux/highmem.h> #include <linux/io.h> @@ -183,8 +182,6 @@ static int cma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) { struct cma_heap_buffer *buffer = dmabuf->priv; - dma_resv_assert_held(dmabuf->resv); - if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0) return -EINVAL; diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c index ee7059399e9c..9076d47ed2ef 100644 --- a/drivers/dma-buf/heaps/system_heap.c +++ b/drivers/dma-buf/heaps/system_heap.c @@ -13,7 +13,6 @@ #include <linux/dma-buf.h> #include <linux/dma-mapping.h> #include <linux/dma-heap.h> -#include <linux/dma-resv.h> #include <linux/err.h> #include <linux/highmem.h> #include <linux/mm.h> @@ -201,8 +200,6 @@ static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) struct sg_page_iter piter; int ret; - dma_resv_assert_held(dmabuf->resv); - for_each_sgtable_page(table, &piter, vma->vm_pgoff) { struct page *page = sg_page_iter_page(&piter); diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index 12cf6bb2e3ce..c40645999648 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -51,8 +51,6 @@ static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma) { struct udmabuf *ubuf = buf->priv; - dma_resv_assert_held(buf->resv); - if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0) return -EINVAL; diff --git a/drivers/firmware/efi/libstub/efi-stub-entry.c b/drivers/firmware/efi/libstub/efi-stub-entry.c index cc4dcaea67fa..2f1902e5d407 100644 --- a/drivers/firmware/efi/libstub/efi-stub-entry.c +++ b/drivers/firmware/efi/libstub/efi-stub-entry.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only #include <linux/efi.h> +#include <linux/screen_info.h> + #include <asm/efi.h> #include "efistub.h" diff --git a/drivers/firmware/efi/libstub/screen_info.c b/drivers/firmware/efi/libstub/screen_info.c index 4be1c4d1f922..a51ec201ca3c 100644 --- a/drivers/firmware/efi/libstub/screen_info.c +++ b/drivers/firmware/efi/libstub/screen_info.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/efi.h> +#include <linux/screen_info.h> + #include <asm/efi.h> #include "efistub.h" diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index afb3b2f5f425..22c1ba9ea28c 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -80,6 +80,7 @@ config DRM_KUNIT_TEST select DRM_BUDDY select DRM_EXPORT_FOR_TESTS if m select DRM_KUNIT_TEST_HELPERS + select DRM_EXEC default KUNIT_ALL_TESTS help This builds unit tests for DRM. This option is not useful for @@ -194,6 +195,12 @@ config DRM_TTM GPU memory types. Will be enabled automatically if a device driver uses it. +config DRM_EXEC + tristate + depends on DRM + help + Execution context for command submissions + config DRM_BUDDY tristate depends on DRM @@ -323,6 +330,8 @@ source "drivers/gpu/drm/v3d/Kconfig" source "drivers/gpu/drm/vc4/Kconfig" +source "drivers/gpu/drm/loongson/Kconfig" + source "drivers/gpu/drm/etnaviv/Kconfig" source "drivers/gpu/drm/hisilicon/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7a09a89b493b..021b3f0ac152 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -78,6 +78,8 @@ obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o # # Memory-management helpers # +# +obj-$(CONFIG_DRM_EXEC) += drm_exec.o obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o @@ -194,3 +196,4 @@ obj-y += gud/ obj-$(CONFIG_DRM_HYPERV) += hyperv/ obj-y += solomon/ obj-$(CONFIG_DRM_SPRD) += sprd/ +obj-$(CONFIG_DRM_LOONGSON) += loongson/ diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig index b91e79c721e2..22d88f8ef527 100644 --- a/drivers/gpu/drm/amd/amdgpu/Kconfig +++ b/drivers/gpu/drm/amd/amdgpu/Kconfig @@ -21,6 +21,7 @@ config DRM_AMDGPU select INTERVAL_TREE select DRM_BUDDY select DRM_SUBALLOC_HELPER + select DRM_EXEC # amdgpu depends on ACPI_VIDEO when ACPI is enabled, for select to work # ACPI_VIDEO's dependencies must also be selected. select INPUT if ACPI diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index a3b86b86dc47..8f2255b3a38a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -53,7 +53,6 @@ #include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> -#include <drm/ttm/ttm_execbuf_util.h> #include <drm/amdgpu_drm.h> #include <drm/drm_gem.h> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index 2d0406bff84e..1e4cc1fe88fc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -25,6 +25,7 @@ #ifndef AMDGPU_AMDKFD_H_INCLUDED #define AMDGPU_AMDKFD_H_INCLUDED +#include <linux/list.h> #include <linux/types.h> #include <linux/mm.h> #include <linux/kthread.h> @@ -32,7 +33,6 @@ #include <linux/mmu_notifier.h> #include <linux/memremap.h> #include <kgd_kfd_interface.h> -#include <drm/ttm/ttm_execbuf_util.h> #include "amdgpu_sync.h" #include "amdgpu_vm.h" #include "amdgpu_xcp.h" @@ -71,8 +71,7 @@ struct kgd_mem { struct hmm_range *range; struct list_head attachments; /* protected by amdkfd_process_info.lock */ - struct ttm_validate_buffer validate_list; - struct ttm_validate_buffer resv_list; + struct list_head validate_list; uint32_t domain; unsigned int mapped_to_gpu_memory; uint64_t va; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index a7f314ddd173..edb35a88cb78 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -27,6 +27,8 @@ #include <linux/sched/task.h> #include <drm/ttm/ttm_tt.h> +#include <drm/drm_exec.h> + #include "amdgpu_object.h" #include "amdgpu_gem.h" #include "amdgpu_vm.h" @@ -964,28 +966,20 @@ static void add_kgd_mem_to_kfd_bo_list(struct kgd_mem *mem, struct amdkfd_process_info *process_info, bool userptr) { - struct ttm_validate_buffer *entry = &mem->validate_list; - struct amdgpu_bo *bo = mem->bo; - - INIT_LIST_HEAD(&entry->head); - entry->num_shared = 1; - entry->bo = &bo->tbo; mutex_lock(&process_info->lock); if (userptr) - list_add_tail(&entry->head, &process_info->userptr_valid_list); + list_add_tail(&mem->validate_list, + &process_info->userptr_valid_list); else - list_add_tail(&entry->head, &process_info->kfd_bo_list); + list_add_tail(&mem->validate_list, &process_info->kfd_bo_list); mutex_unlock(&process_info->lock); } static void remove_kgd_mem_from_kfd_bo_list(struct kgd_mem *mem, struct amdkfd_process_info *process_info) { - struct ttm_validate_buffer *bo_list_entry; - - bo_list_entry = &mem->validate_list; mutex_lock(&process_info->lock); - list_del(&bo_list_entry->head); + list_del(&mem->validate_list); mutex_unlock(&process_info->lock); } @@ -1072,13 +1066,12 @@ out: * object can track VM updates. */ struct bo_vm_reservation_context { - struct amdgpu_bo_list_entry kfd_bo; /* BO list entry for the KFD BO */ - unsigned int n_vms; /* Number of VMs reserved */ - struct amdgpu_bo_list_entry *vm_pd; /* Array of VM BO list entries */ - struct ww_acquire_ctx ticket; /* Reservation ticket */ - struct list_head list, duplicates; /* BO lists */ - struct amdgpu_sync *sync; /* Pointer to sync object */ - bool reserved; /* Whether BOs are reserved */ + /* DRM execution context for the reservation */ + struct drm_exec exec; + /* Number of VMs reserved */ + unsigned int n_vms; + /* Pointer to sync object */ + struct amdgpu_sync *sync; }; enum bo_vm_match { @@ -1102,35 +1095,26 @@ static int reserve_bo_and_vm(struct kgd_mem *mem, WARN_ON(!vm); - ctx->reserved = false; ctx->n_vms = 1; ctx->sync = &mem->sync; - - INIT_LIST_HEAD(&ctx->list); - INIT_LIST_HEAD(&ctx->duplicates); - - ctx->vm_pd = kcalloc(ctx->n_vms, sizeof(*ctx->vm_pd), GFP_KERNEL); - if (!ctx->vm_pd) - return -ENOMEM; - - ctx->kfd_bo.priority = 0; - ctx->kfd_bo.tv.bo = &bo->tbo; - ctx->kfd_bo.tv.num_shared = 1; - list_add(&ctx->kfd_bo.tv.head, &ctx->list); - - amdgpu_vm_get_pd_bo(vm, &ctx->list, &ctx->vm_pd[0]); - - ret = ttm_eu_reserve_buffers(&ctx->ticket, &ctx->list, - false, &ctx->duplicates); - if (ret) { - pr_err("Failed to reserve buffers in ttm.\n"); - kfree(ctx->vm_pd); - ctx->vm_pd = NULL; - return ret; + drm_exec_init(&ctx->exec, DRM_EXEC_INTERRUPTIBLE_WAIT); + drm_exec_until_all_locked(&ctx->exec) { + ret = amdgpu_vm_lock_pd(vm, &ctx->exec, 2); + drm_exec_retry_on_contention(&ctx->exec); + if (unlikely(ret)) + goto error; + + ret = drm_exec_lock_obj(&ctx->exec, &bo->tbo.base); + drm_exec_retry_on_contention(&ctx->exec); + if (unlikely(ret)) + goto error; } - - ctx->reserved = true; return 0; + +error: + pr_err("Failed to reserve buffers in ttm.\n"); + drm_exec_fini(&ctx->exec); + return ret; } /** @@ -1147,63 +1131,39 @@ static int reserve_bo_and_cond_vms(struct kgd_mem *mem, struct amdgpu_vm *vm, enum bo_vm_match map_type, struct bo_vm_reservation_context *ctx) { - struct amdgpu_bo *bo = mem->bo; struct kfd_mem_attachment *entry; - unsigned int i; + struct amdgpu_bo *bo = mem->bo; int ret; - ctx->reserved = false; - ctx->n_vms = 0; - ctx->vm_pd = NULL; ctx->sync = &mem->sync; + drm_exec_init(&ctx->exec, DRM_EXEC_INTERRUPTIBLE_WAIT); + drm_exec_until_all_locked(&ctx->exec) { + ctx->n_vms = 0; + list_for_each_entry(entry, &mem->attachments, list) { + if ((vm && vm != entry->bo_va->base.vm) || + (entry->is_mapped != map_type + && map_type != BO_VM_ALL)) + continue; - INIT_LIST_HEAD(&ctx->list); - INIT_LIST_HEAD(&ctx->duplicates); - - list_for_each_entry(entry, &mem->attachments, list) { - if ((vm && vm != entry->bo_va->base.vm) || - (entry->is_mapped != map_type - && map_type != BO_VM_ALL)) - continue; - - ctx->n_vms++; - } - - if (ctx->n_vms != 0) { - ctx->vm_pd = kcalloc(ctx->n_vms, sizeof(*ctx->vm_pd), - GFP_KERNEL); - if (!ctx->vm_pd) - return -ENOMEM; - } - - ctx->kfd_bo.priority = 0; - ctx->kfd_bo.tv.bo = &bo->tbo; - ctx->kfd_bo.tv.num_shared = 1; - list_add(&ctx->kfd_bo.tv.head, &ctx->list); - - i = 0; - list_for_each_entry(entry, &mem->attachments, list) { - if ((vm && vm != entry->bo_va->base.vm) || - (entry->is_mapped != map_type - && map_type != BO_VM_ALL)) - continue; - - amdgpu_vm_get_pd_bo(entry->bo_va->base.vm, &ctx->list, - &ctx->vm_pd[i]); - i++; - } + ret = amdgpu_vm_lock_pd(entry->bo_va->base.vm, + &ctx->exec, 2); + drm_exec_retry_on_contention(&ctx->exec); + if (unlikely(ret)) + goto error; + ++ctx->n_vms; + } - ret = ttm_eu_reserve_buffers(&ctx->ticket, &ctx->list, - false, &ctx->duplicates); - if (ret) { - pr_err("Failed to reserve buffers in ttm.\n"); - kfree(ctx->vm_pd); - ctx->vm_pd = NULL; - return ret; + ret = drm_exec_prepare_obj(&ctx->exec, &bo->tbo.base, 1); + drm_exec_retry_on_contention(&ctx->exec); + if (unlikely(ret)) + goto error; } - - ctx->reserved = true; return 0; + +error: + pr_err("Failed to reserve buffers in ttm.\n"); + drm_exec_fini(&ctx->exec); + return ret; } /** @@ -1224,15 +1184,8 @@ static int unreserve_bo_and_vms(struct bo_vm_reservation_context *ctx, if (wait) ret = amdgpu_sync_wait(ctx->sync, intr); - if (ctx->reserved) - ttm_eu_backoff_reservation(&ctx->ticket, &ctx->list); - kfree(ctx->vm_pd); - + drm_exec_fini(&ctx->exec); ctx->sync = NULL; - - ctx->reserved = false; - ctx->vm_pd = NULL; - return ret; } @@ -1854,7 +1807,6 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( bool use_release_notifier = (mem->bo->kfd_bo == mem); struct kfd_mem_attachment *entry, *tmp; struct bo_vm_reservation_context ctx; - struct ttm_validate_buffer *bo_list_entry; unsigned int mapped_to_gpu_memory; int ret; bool is_imported = false; @@ -1882,9 +1834,8 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( } /* Make sure restore workers don't access the BO any more */ - bo_list_entry = &mem->validate_list; mutex_lock(&process_info->lock); - list_del(&bo_list_entry->head); + list_del(&mem->validate_list); mutex_unlock(&process_info->lock); /* Cleanup user pages and MMU notifiers */ @@ -2451,14 +2402,14 @@ static int update_invalid_user_pages(struct amdkfd_process_info *process_info, /* Move all invalidated BOs to the userptr_inval_list */ list_for_each_entry_safe(mem, tmp_mem, &process_info->userptr_valid_list, - validate_list.head) + validate_list) if (mem->invalid) - list_move_tail(&mem->validate_list.head, + list_move_tail(&mem->validate_list, &process_info->userptr_inval_list); /* Go through userptr_inval_list and update any invalid user_pages */ list_for_each_entry(mem, &process_info->userptr_inval_list, - validate_list.head) { + validate_list) { invalid = mem->invalid; if (!invalid) /* BO hasn't been invalidated since the last @@ -2538,50 +2489,41 @@ unlock_out: */ static int validate_invalid_user_pages(struct amdkfd_process_info *process_info) { - struct amdgpu_bo_list_entry *pd_bo_list_entries; - struct list_head resv_list, duplicates; - struct ww_acquire_ctx ticket; + struct ttm_operation_ctx ctx = { false, false }; struct amdgpu_sync sync; + struct drm_exec exec; struct amdgpu_vm *peer_vm; struct kgd_mem *mem, *tmp_mem; struct amdgpu_bo *bo; - struct ttm_operation_ctx ctx = { false, false }; - int i, ret; - - pd_bo_list_entries = kcalloc(process_info->n_vms, - sizeof(struct amdgpu_bo_list_entry), - GFP_KERNEL); - if (!pd_bo_list_entries) { - pr_err("%s: Failed to allocate PD BO list entries\n", __func__); - ret = -ENOMEM; - goto out_no_mem; - } - - INIT_LIST_HEAD(&resv_list); - INIT_LIST_HEAD(&duplicates); + int ret; - /* Get all the page directory BOs that need to be reserved */ - i = 0; - list_for_each_entry(peer_vm, &process_info->vm_list_head, - vm_list_node) - amdgpu_vm_get_pd_bo(peer_vm, &resv_list, - &pd_bo_list_entries[i++]); - /* Add the userptr_inval_list entries to resv_list */ - list_for_each_entry(mem, &process_info->userptr_inval_list, - validate_list.head) { - list_add_tail(&mem->resv_list.head, &resv_list); - mem->resv_list.bo = mem->validate_list.bo; - mem->resv_list.num_shared = mem->validate_list.num_shared; - } + amdgpu_sync_create(&sync); + drm_exec_init(&exec, 0); /* Reserve all BOs and page tables for validation */ - ret = ttm_eu_reserve_buffers(&ticket, &resv_list, false, &duplicates); - WARN(!list_empty(&duplicates), "Duplicates should be empty"); - if (ret) - goto out_free; + drm_exec_until_all_locked(&exec) { + /* Reserve all the page directories */ + list_for_each_entry(peer_vm, &process_info->vm_list_head, + vm_list_node) { + ret = amdgpu_vm_lock_pd(peer_vm, &exec, 2); + drm_exec_retry_on_contention(&exec); + if (unlikely(ret)) + goto unreserve_out; + } - amdgpu_sync_create(&sync); + /* Reserve the userptr_inval_list entries to resv_list */ + list_for_each_entry(mem, &process_info->userptr_inval_list, + validate_list) { + struct drm_gem_object *gobj; + + gobj = &mem->bo->tbo.base; + ret = drm_exec_prepare_obj(&exec, gobj, 1); + drm_exec_retry_on_contention(&exec); + if (unlikely(ret)) + goto unreserve_out; + } + } ret = process_validate_vms(process_info); if (ret) @@ -2590,7 +2532,7 @@ static int validate_invalid_user_pages(struct amdkfd_process_info *process_info) /* Validate BOs and update GPUVM page tables */ list_for_each_entry_safe(mem, tmp_mem, &process_info->userptr_inval_list, - validate_list.head) { + validate_list) { struct kfd_mem_attachment *attachment; bo = mem->bo; @@ -2632,12 +2574,9 @@ static int validate_invalid_user_pages(struct amdkfd_process_info *process_info) ret = process_update_pds(process_info, &sync); unreserve_out: - ttm_eu_backoff_reservation(&ticket, &resv_list); + drm_exec_fini(&exec); amdgpu_sync_wait(&sync, false); amdgpu_sync_free(&sync); -out_free: - kfree(pd_bo_list_entries); -out_no_mem: return ret; } @@ -2653,7 +2592,7 @@ static int confirm_valid_user_pages_locked(struct amdkfd_process_info *process_i list_for_each_entry_safe(mem, tmp_mem, &process_info->userptr_inval_list, - validate_list.head) { + validate_list) { bool valid; /* keep mem without hmm range at userptr_inval_list */ @@ -2677,7 +2616,7 @@ static int confirm_valid_user_pages_locked(struct amdkfd_process_info *process_i continue; } - list_move_tail(&mem->validate_list.head, + list_move_tail(&mem->validate_list, &process_info->userptr_valid_list); } @@ -2787,50 +2726,44 @@ unlock_out: */ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef) { - struct amdgpu_bo_list_entry *pd_bo_list; struct amdkfd_process_info *process_info = info; struct amdgpu_vm *peer_vm; struct kgd_mem *mem; - struct bo_vm_reservation_context ctx; struct amdgpu_amdkfd_fence *new_fence; - int ret = 0, i; struct list_head duplicate_save; struct amdgpu_sync sync_obj; unsigned long failed_size = 0; unsigned long total_size = 0; + struct drm_exec exec; + int ret; INIT_LIST_HEAD(&duplicate_save); - INIT_LIST_HEAD(&ctx.list); - INIT_LIST_HEAD(&ctx.duplicates); - - pd_bo_list = kcalloc(process_info->n_vms, - sizeof(struct amdgpu_bo_list_entry), - GFP_KERNEL); - if (!pd_bo_list) - return -ENOMEM; - i = 0; mutex_lock(&process_info->lock); - list_for_each_entry(peer_vm, &process_info->vm_list_head, - vm_list_node) - amdgpu_vm_get_pd_bo(peer_vm, &ctx.list, &pd_bo_list[i++]); - /* Reserve all BOs and page tables/directory. Add all BOs from - * kfd_bo_list to ctx.list - */ - list_for_each_entry(mem, &process_info->kfd_bo_list, - validate_list.head) { - - list_add_tail(&mem->resv_list.head, &ctx.list); - mem->resv_list.bo = mem->validate_list.bo; - mem->resv_list.num_shared = mem->validate_list.num_shared; - } + drm_exec_init(&exec, 0); + drm_exec_until_all_locked(&exec) { + list_for_each_entry(peer_vm, &process_info->vm_list_head, + vm_list_node) { + ret = amdgpu_vm_lock_pd(peer_vm, &exec, 2); + drm_exec_retry_on_contention(&exec); + if (unlikely(ret)) + goto ttm_reserve_fail; + } - ret = ttm_eu_reserve_buffers(&ctx.ticket, &ctx.list, - false, &duplicate_save); - if (ret) { - pr_debug("Memory eviction: TTM Reserve Failed. Try again\n"); - goto ttm_reserve_fail; + /* Reserve all BOs and page tables/directory. Add all BOs from + * kfd_bo_list to ctx.list + */ + list_for_each_entry(mem, &process_info->kfd_bo_list, + validate_list) { + struct drm_gem_object *gobj; + + gobj = &mem->bo->tbo.base; + ret = drm_exec_prepare_obj(&exec, gobj, 1); + drm_exec_retry_on_contention(&exec); + if (unlikely(ret)) + goto ttm_reserve_fail; + } } amdgpu_sync_create(&sync_obj); @@ -2848,7 +2781,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef) /* Validate BOs and map them to GPUVM (update VM page tables). */ list_for_each_entry(mem, &process_info->kfd_bo_list, - validate_list.head) { + validate_list) { struct amdgpu_bo *bo = mem->bo; uint32_t domain = mem->domain; @@ -2924,8 +2857,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef) *ef = dma_fence_get(&new_fence->base); /* Attach new eviction fence to all BOs except pinned ones */ - list_for_each_entry(mem, &process_info->kfd_bo_list, - validate_list.head) { + list_for_each_entry(mem, &process_info->kfd_bo_list, validate_list) { if (mem->bo->tbo.pin_count) continue; @@ -2944,11 +2876,10 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef) } validate_map_fail: - ttm_eu_backoff_reservation(&ctx.ticket, &ctx.list); amdgpu_sync_free(&sync_obj); ttm_reserve_fail: + drm_exec_fini(&exec); mutex_unlock(&process_info->lock); - kfree(pd_bo_list); return ret; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c index 252a876b0725..b6298e901cbd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c @@ -28,6 +28,7 @@ * Christian König <deathsimple@vodafone.de> */ +#include <linux/sort.h> #include <linux/uaccess.h> #include "amdgpu.h" @@ -50,13 +51,20 @@ static void amdgpu_bo_list_free(struct kref *ref) refcount); struct amdgpu_bo_list_entry *e; - amdgpu_bo_list_for_each_entry(e, list) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo); + amdgpu_bo_list_for_each_entry(e, list) + amdgpu_bo_unref(&e->bo); + call_rcu(&list->rhead, amdgpu_bo_list_free_rcu); +} - amdgpu_bo_unref(&bo); - } +static int amdgpu_bo_list_entry_cmp(const void *_a, const void *_b) +{ + const struct amdgpu_bo_list_entry *a = _a, *b = _b; - call_rcu(&list->rhead, amdgpu_bo_list_free_rcu); + if (a->priority > b->priority) + return 1; + if (a->priority < b->priority) + return -1; + return 0; } int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp, @@ -118,7 +126,7 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp, entry->priority = min(info[i].bo_priority, AMDGPU_BO_LIST_MAX_PRIORITY); - entry->tv.bo = &bo->tbo; + entry->bo = bo; if (bo->preferred_domains == AMDGPU_GEM_DOMAIN_GDS) list->gds_obj = bo; @@ -133,6 +141,8 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp, list->first_userptr = first_userptr; list->num_entries = num_entries; + sort(array, last_entry, sizeof(struct amdgpu_bo_list_entry), + amdgpu_bo_list_entry_cmp, NULL); trace_amdgpu_cs_bo_status(list->num_entries, total_size); @@ -141,16 +151,10 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp, return 0; error_free: - for (i = 0; i < last_entry; ++i) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(array[i].tv.bo); - - amdgpu_bo_unref(&bo); - } - for (i = first_userptr; i < num_entries; ++i) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(array[i].tv.bo); - - amdgpu_bo_unref(&bo); - } + for (i = 0; i < last_entry; ++i) + amdgpu_bo_unref(&array[i].bo); + for (i = first_userptr; i < num_entries; ++i) + amdgpu_bo_unref(&array[i].bo); kvfree(list); return r; @@ -182,41 +186,6 @@ int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id, return -ENOENT; } -void amdgpu_bo_list_get_list(struct amdgpu_bo_list *list, - struct list_head *validated) -{ - /* This is based on the bucket sort with O(n) time complexity. - * An item with priority "i" is added to bucket[i]. The lists are then - * concatenated in descending order. - */ - struct list_head bucket[AMDGPU_BO_LIST_NUM_BUCKETS]; - struct amdgpu_bo_list_entry *e; - unsigned i; - - for (i = 0; i < AMDGPU_BO_LIST_NUM_BUCKETS; i++) - INIT_LIST_HEAD(&bucket[i]); - - /* Since buffers which appear sooner in the relocation list are - * likely to be used more often than buffers which appear later - * in the list, the sort mustn't change the ordering of buffers - * with the same priority, i.e. it must be stable. - */ - amdgpu_bo_list_for_each_entry(e, list) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo); - unsigned priority = e->priority; - - if (!bo->parent) - list_add_tail(&e->tv.head, &bucket[priority]); - - e->user_pages = NULL; - e->range = NULL; - } - - /* Connect the sorted buckets in the output list. */ - for (i = 0; i < AMDGPU_BO_LIST_NUM_BUCKETS; i++) - list_splice(&bucket[i], validated); -} - void amdgpu_bo_list_put(struct amdgpu_bo_list *list) { kref_put(&list->refcount, amdgpu_bo_list_free); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h index ededdc01ca28..26c01cb131f2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h @@ -23,7 +23,6 @@ #ifndef __AMDGPU_BO_LIST_H__ #define __AMDGPU_BO_LIST_H__ -#include <drm/ttm/ttm_execbuf_util.h> #include <drm/amdgpu_drm.h> struct hmm_range; @@ -36,7 +35,7 @@ struct amdgpu_bo_va; struct amdgpu_fpriv; struct amdgpu_bo_list_entry { - struct ttm_validate_buffer tv; + struct amdgpu_bo *bo; struct amdgpu_bo_va *bo_va; uint32_t priority; struct page **user_pages; @@ -60,8 +59,6 @@ struct amdgpu_bo_list { int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id, struct amdgpu_bo_list **result); -void amdgpu_bo_list_get_list(struct amdgpu_bo_list *list, - struct list_head *validated); void amdgpu_bo_list_put(struct amdgpu_bo_list *list); int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in, struct drm_amdgpu_bo_list_entry **info_param); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 040f4cb6ab2d..977e1804718d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -65,6 +65,7 @@ static int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, } amdgpu_sync_create(&p->sync); + drm_exec_init(&p->exec, DRM_EXEC_INTERRUPTIBLE_WAIT); return 0; } @@ -125,7 +126,6 @@ static int amdgpu_cs_p1_user_fence(struct amdgpu_cs_parser *p, uint32_t *offset) { struct drm_gem_object *gobj; - struct amdgpu_bo *bo; unsigned long size; int r; @@ -133,18 +133,16 @@ static int amdgpu_cs_p1_user_fence(struct amdgpu_cs_parser *p, if (gobj == NULL) return -EINVAL; - bo = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj)); - p->uf_entry.priority = 0; - p->uf_entry.tv.bo = &bo->tbo; + p->uf_bo = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj)); drm_gem_object_put(gobj); - size = amdgpu_bo_size(bo); + size = amdgpu_bo_size(p->uf_bo); if (size != PAGE_SIZE || (data->offset + 8) > size) { r = -EINVAL; goto error_unref; } - if (amdgpu_ttm_tt_get_usermm(bo->tbo.ttm)) { + if (amdgpu_ttm_tt_get_usermm(p->uf_bo->tbo.ttm)) { r = -EINVAL; goto error_unref; } @@ -154,7 +152,7 @@ static int amdgpu_cs_p1_user_fence(struct amdgpu_cs_parser *p, return 0; error_unref: - amdgpu_bo_unref(&bo); + amdgpu_bo_unref(&p->uf_bo); return r; } @@ -311,7 +309,7 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p, goto free_all_kdata; } - if (p->uf_entry.tv.bo) + if (p->uf_bo) p->gang_leader->uf_addr = uf_offset; kvfree(chunk_array); @@ -356,7 +354,7 @@ static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p, ib = &job->ibs[job->num_ibs++]; /* MM engine doesn't support user fences */ - if (p->uf_entry.tv.bo && ring->funcs->no_user_fence) + if (p->uf_bo && ring->funcs->no_user_fence) return -EINVAL; if (chunk_ib->ip_type == AMDGPU_HW_IP_GFX && @@ -841,55 +839,18 @@ retry: return r; } -static int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p, - struct list_head *validated) -{ - struct ttm_operation_ctx ctx = { true, false }; - struct amdgpu_bo_list_entry *lobj; - int r; - - list_for_each_entry(lobj, validated, tv.head) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(lobj->tv.bo); - struct mm_struct *usermm; - - usermm = amdgpu_ttm_tt_get_usermm(bo->tbo.ttm); - if (usermm && usermm != current->mm) - return -EPERM; - - if (amdgpu_ttm_tt_is_userptr(bo->tbo.ttm) && - lobj->user_invalidated && lobj->user_pages) { - amdgpu_bo_placement_from_domain(bo, - AMDGPU_GEM_DOMAIN_CPU); - r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); - if (r) - return r; - - amdgpu_ttm_tt_set_user_pages(bo->tbo.ttm, - lobj->user_pages); - } - - r = amdgpu_cs_bo_validate(p, bo); - if (r) - return r; - - kvfree(lobj->user_pages); - lobj->user_pages = NULL; - } - return 0; -} - static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, union drm_amdgpu_cs *cs) { struct amdgpu_fpriv *fpriv = p->filp->driver_priv; + struct ttm_operation_ctx ctx = { true, false }; struct amdgpu_vm *vm = &fpriv->vm; struct amdgpu_bo_list_entry *e; - struct list_head duplicates; + struct drm_gem_object *obj; + unsigned long index; unsigned int i; int r; - INIT_LIST_HEAD(&p->validated); - /* p->bo_list could already be assigned if AMDGPU_CHUNK_ID_BO_HANDLES is present */ if (cs->in.bo_list_handle) { if (p->bo_list) @@ -909,29 +870,13 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, mutex_lock(&p->bo_list->bo_list_mutex); - /* One for TTM and one for each CS job */ - amdgpu_bo_list_for_each_entry(e, p->bo_list) - e->tv.num_shared = 1 + p->gang_size; - p->uf_entry.tv.num_shared = 1 + p->gang_size; - - amdgpu_bo_list_get_list(p->bo_list, &p->validated); - - INIT_LIST_HEAD(&duplicates); - amdgpu_vm_get_pd_bo(&fpriv->vm, &p->validated, &p->vm_pd); - - /* Two for VM updates, one for TTM and one for each CS job */ - p->vm_pd.tv.num_shared = 3 + p->gang_size; - - if (p->uf_entry.tv.bo && !ttm_to_amdgpu_bo(p->uf_entry.tv.bo)->parent) - list_add(&p->uf_entry.tv.head, &p->validated); - /* Get userptr backing pages. If pages are updated after registered * in amdgpu_gem_userptr_ioctl(), amdgpu_cs_list_validate() will do * amdgpu_ttm_backend_bind() to flush and invalidate new pages */ amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo); bool userpage_invalidated = false; + struct amdgpu_bo *bo = e->bo; int i; e->user_pages = kvmalloc_array(bo->tbo.ttm->num_pages, @@ -959,18 +904,56 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, e->user_invalidated = userpage_invalidated; } - r = ttm_eu_reserve_buffers(&p->ticket, &p->validated, true, - &duplicates); - if (unlikely(r != 0)) { - if (r != -ERESTARTSYS) - DRM_ERROR("ttm_eu_reserve_buffers failed.\n"); - goto out_free_user_pages; + drm_exec_until_all_locked(&p->exec) { + r = amdgpu_vm_lock_pd(&fpriv->vm, &p->exec, 1 + p->gang_size); + drm_exec_retry_on_contention(&p->exec); + if (unlikely(r)) + goto out_free_user_pages; + + amdgpu_bo_list_for_each_entry(e, p->bo_list) { + /* One fence for TTM and one for each CS job */ + r = drm_exec_prepare_obj(&p->exec, &e->bo->tbo.base, + 1 + p->gang_size); + drm_exec_retry_on_contention(&p->exec); + if (unlikely(r)) + goto out_free_user_pages; + + e->bo_va = amdgpu_vm_bo_find(vm, e->bo); + } + + if (p->uf_bo) { + r = drm_exec_prepare_obj(&p->exec, &p->uf_bo->tbo.base, + 1 + p->gang_size); + drm_exec_retry_on_contention(&p->exec); + if (unlikely(r)) + goto out_free_user_pages; + } } - amdgpu_bo_list_for_each_entry(e, p->bo_list) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo); + amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) { + struct mm_struct *usermm; - e->bo_va = amdgpu_vm_bo_find(vm, bo); + usermm = amdgpu_ttm_tt_get_usermm(e->bo->tbo.ttm); + if (usermm && usermm != current->mm) { + r = -EPERM; + goto out_free_user_pages; + } + + if (amdgpu_ttm_tt_is_userptr(e->bo->tbo.ttm) && + e->user_invalidated && e->user_pages) { + amdgpu_bo_placement_from_domain(e->bo, + AMDGPU_GEM_DOMAIN_CPU); + r = ttm_bo_validate(&e->bo->tbo, &e->bo->placement, + &ctx); + if (r) + goto out_free_user_pages; + + amdgpu_ttm_tt_set_user_pages(e->bo->tbo.ttm, + e->user_pages); + } + + kvfree(e->user_pages); + e->user_pages = NULL; } amdgpu_cs_get_threshold_for_moves(p->adev, &p->bytes_moved_threshold, @@ -982,25 +965,21 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, amdgpu_cs_bo_validate, p); if (r) { DRM_ERROR("amdgpu_vm_validate_pt_bos() failed.\n"); - goto error_validate; + goto out_free_user_pages; } - r = amdgpu_cs_list_validate(p, &duplicates); - if (r) - goto error_validate; - - r = amdgpu_cs_list_validate(p, &p->validated); - if (r) - goto error_validate; - - if (p->uf_entry.tv.bo) { - struct amdgpu_bo *uf = ttm_to_amdgpu_bo(p->uf_entry.tv.bo); + drm_exec_for_each_locked_object(&p->exec, index, obj) { + r = amdgpu_cs_bo_validate(p, gem_to_amdgpu_bo(obj)); + if (unlikely(r)) + goto out_free_user_pages; + } - r = amdgpu_ttm_alloc_gart(&uf->tbo); - if (r) - goto error_validate; + if (p->uf_bo) { + r = amdgpu_ttm_alloc_gart(&p->uf_bo->tbo); + if (unlikely(r)) + goto out_free_user_pages; - p->gang_leader->uf_addr += amdgpu_bo_gpu_offset(uf); + p->gang_leader->uf_addr += amdgpu_bo_gpu_offset(p->uf_bo); } amdgpu_cs_report_moved_bytes(p->adev, p->bytes_moved, @@ -1012,12 +991,9 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, p->bo_list->oa_obj); return 0; -error_validate: - ttm_eu_backoff_reservation(&p->ticket, &p->validated); - out_free_user_pages: amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo); + struct amdgpu_bo *bo = e->bo; if (!e->user_pages) continue; @@ -1123,7 +1099,6 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) struct amdgpu_vm *vm = &fpriv->vm; struct amdgpu_bo_list_entry *e; struct amdgpu_bo_va *bo_va; - struct amdgpu_bo *bo; unsigned int i; int r; @@ -1152,11 +1127,6 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) } amdgpu_bo_list_for_each_entry(e, p->bo_list) { - /* ignore duplicates */ - bo = ttm_to_amdgpu_bo(e->tv.bo); - if (!bo) - continue; - bo_va = e->bo_va; if (bo_va == NULL) continue; @@ -1194,7 +1164,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) if (amdgpu_vm_debug) { /* Invalidate all BOs to test for userspace bugs */ amdgpu_bo_list_for_each_entry(e, p->bo_list) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo); + struct amdgpu_bo *bo = e->bo; /* ignore duplicates */ if (!bo) @@ -1211,8 +1181,9 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) { struct amdgpu_fpriv *fpriv = p->filp->driver_priv; struct drm_gpu_scheduler *sched; - struct amdgpu_bo_list_entry *e; + struct drm_gem_object *obj; struct dma_fence *fence; + unsigned long index; unsigned int i; int r; @@ -1223,8 +1194,9 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) return r; } - list_for_each_entry(e, &p->validated, tv.head) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo); + drm_exec_for_each_locked_object(&p->exec, index, obj) { + struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); + struct dma_resv *resv = bo->tbo.base.resv; enum amdgpu_sync_mode sync_mode; @@ -1288,6 +1260,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, struct amdgpu_fpriv *fpriv = p->filp->driver_priv; struct amdgpu_job *leader = p->gang_leader; struct amdgpu_bo_list_entry *e; + struct drm_gem_object *gobj; + unsigned long index; unsigned int i; uint64_t seq; int r; @@ -1326,9 +1300,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, */ r = 0; amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) { - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo); - - r |= !amdgpu_ttm_tt_get_user_pages_done(bo->tbo.ttm, e->range); + r |= !amdgpu_ttm_tt_get_user_pages_done(e->bo->tbo.ttm, + e->range); e->range = NULL; } if (r) { @@ -1338,20 +1311,22 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, } p->fence = dma_fence_get(&leader->base.s_fence->finished); - list_for_each_entry(e, &p->validated, tv.head) { + drm_exec_for_each_locked_object(&p->exec, index, gobj) { + + ttm_bo_move_to_lru_tail_unlocked(&gem_to_amdgpu_bo(gobj)->tbo); /* Everybody except for the gang leader uses READ */ for (i = 0; i < p->gang_size; ++i) { if (p->jobs[i] == leader) continue; - dma_resv_add_fence(e->tv.bo->base.resv, + dma_resv_add_fence(gobj->resv, &p->jobs[i]->base.s_fence->finished, DMA_RESV_USAGE_READ); } - /* The gang leader is remembered as writer */ - e->tv.num_shared = 0; + /* The gang leader as remembered as writer */ + dma_resv_add_fence(gobj->resv, p->fence, DMA_RESV_USAGE_WRITE); } seq = amdgpu_ctx_add_fence(p->ctx, p->entities[p->gang_leader_idx], @@ -1367,7 +1342,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, cs->out.handle = seq; leader->uf_sequence = seq; - amdgpu_vm_bo_trace_cs(&fpriv->vm, &p->ticket); + amdgpu_vm_bo_trace_cs(&fpriv->vm, &p->exec.ticket); for (i = 0; i < p->gang_size; ++i) { amdgpu_job_free_resources(p->jobs[i]); trace_amdgpu_cs_ioctl(p->jobs[i]); @@ -1376,7 +1351,6 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, } amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm); - ttm_eu_fence_buffer_objects(&p->ticket, &p->validated, p->fence); mutex_unlock(&p->adev->notifier_lock); mutex_unlock(&p->bo_list->bo_list_mutex); @@ -1389,6 +1363,8 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) unsigned int i; amdgpu_sync_free(&parser->sync); + drm_exec_fini(&parser->exec); + for (i = 0; i < parser->num_post_deps; i++) { drm_syncobj_put(parser->post_deps[i].syncobj); kfree(parser->post_deps[i].chain); @@ -1409,11 +1385,7 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) if (parser->jobs[i]) amdgpu_job_free(parser->jobs[i]); } - if (parser->uf_entry.tv.bo) { - struct amdgpu_bo *uf = ttm_to_amdgpu_bo(parser->uf_entry.tv.bo); - - amdgpu_bo_unref(&uf); - } + amdgpu_bo_unref(&parser->uf_bo); } int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) @@ -1474,7 +1446,6 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return 0; error_backoff: - ttm_eu_backoff_reservation(&parser.ticket, &parser.validated); mutex_unlock(&parser.bo_list->bo_list_mutex); error_fini: @@ -1809,7 +1780,7 @@ int amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser, *map = mapping; /* Double check that the BO is reserved by this CS */ - if (dma_resv_locking_ctx((*bo)->tbo.base.resv) != &parser->ticket) + if (dma_resv_locking_ctx((*bo)->tbo.base.resv) != &parser->exec.ticket) return -EINVAL; if (!((*bo)->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS)) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.h index fb3e3d56d427..39c33ad100cb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.h @@ -24,6 +24,7 @@ #define __AMDGPU_CS_H__ #include <linux/ww_mutex.h> +#include <drm/drm_exec.h> #include "amdgpu_job.h" #include "amdgpu_bo_list.h" @@ -62,11 +63,9 @@ struct amdgpu_cs_parser { struct amdgpu_job *gang_leader; /* buffer objects */ - struct ww_acquire_ctx ticket; + struct drm_exec exec; struct amdgpu_bo_list *bo_list; struct amdgpu_mn *mn; - struct amdgpu_bo_list_entry vm_pd; - struct list_head validated; struct dma_fence *fence; uint64_t bytes_moved_threshold; uint64_t bytes_moved_vis_threshold; @@ -74,7 +73,7 @@ struct amdgpu_cs_parser { uint64_t bytes_moved_vis; /* user fence */ - struct amdgpu_bo_list_entry uf_entry; + struct amdgpu_bo *uf_bo; unsigned num_post_deps; struct amdgpu_cs_post_dep *post_deps; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c index 23d054526e7c..720011019741 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c @@ -22,6 +22,8 @@ * * Author: Monk.liu@amd.com */ +#include <drm/drm_exec.h> + #include "amdgpu.h" uint64_t amdgpu_csa_vaddr(struct amdgpu_device *adev) @@ -65,31 +67,25 @@ int amdgpu_map_static_csa(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct amdgpu_bo *bo, struct amdgpu_bo_va **bo_va, uint64_t csa_addr, uint32_t size) { - struct ww_acquire_ctx ticket; - struct list_head list; - struct amdgpu_bo_list_entry pd; - struct ttm_validate_buffer csa_tv; + struct drm_exec exec; int r; - INIT_LIST_HEAD(&list); - INIT_LIST_HEAD(&csa_tv.head); - csa_tv.bo = &bo->tbo; - csa_tv.num_shared = 1; - - list_add(&csa_tv.head, &list); - amdgpu_vm_get_pd_bo(vm, &list, &pd); - - r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); - if (r) { - DRM_ERROR("failed to reserve CSA,PD BOs: err=%d\n", r); - return r; + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); + drm_exec_until_all_locked(&exec) { + r = amdgpu_vm_lock_pd(vm, &exec, 0); + if (likely(!r)) + r = drm_exec_lock_obj(&exec, &bo->tbo.base); + drm_exec_retry_on_contention(&exec); + if (unlikely(r)) { + DRM_ERROR("failed to reserve CSA,PD BOs: err=%d\n", r); + goto error; + } } *bo_va = amdgpu_vm_bo_add(adev, vm, bo); if (!*bo_va) { - ttm_eu_backoff_reservation(&ticket, &list); - DRM_ERROR("failed to create bo_va for static CSA\n"); - return -ENOMEM; + r = -ENOMEM; + goto error; } r = amdgpu_vm_bo_map(adev, *bo_va, csa_addr, 0, size, @@ -99,48 +95,42 @@ int amdgpu_map_static_csa(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (r) { DRM_ERROR("failed to do bo_map on static CSA, err=%d\n", r); amdgpu_vm_bo_del(adev, *bo_va); - ttm_eu_backoff_reservation(&ticket, &list); - return r; + goto error; } - ttm_eu_backoff_reservation(&ticket, &list); - return 0; +error: + drm_exec_fini(&exec); + return r; } int amdgpu_unmap_static_csa(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct amdgpu_bo *bo, struct amdgpu_bo_va *bo_va, uint64_t csa_addr) { - struct ww_acquire_ctx ticket; - struct list_head list; - struct amdgpu_bo_list_entry pd; - struct ttm_validate_buffer csa_tv; + struct drm_exec exec; int r; - INIT_LIST_HEAD(&list); - INIT_LIST_HEAD(&csa_tv.head); - csa_tv.bo = &bo->tbo; - csa_tv.num_shared = 1; - - list_add(&csa_tv.head, &list); - amdgpu_vm_get_pd_bo(vm, &list, &pd); - - r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); - if (r) { - DRM_ERROR("failed to reserve CSA,PD BOs: err=%d\n", r); - return r; + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); + drm_exec_until_all_locked(&exec) { + r = amdgpu_vm_lock_pd(vm, &exec, 0); + if (likely(!r)) + r = drm_exec_lock_obj(&exec, &bo->tbo.base); + drm_exec_retry_on_contention(&exec); + if (unlikely(r)) { + DRM_ERROR("failed to reserve CSA,PD BOs: err=%d\n", r); + goto error; + } } r = amdgpu_vm_bo_unmap(adev, bo_va, csa_addr); if (r) { DRM_ERROR("failed to do bo_unmap on static CSA, err=%d\n", r); - ttm_eu_backoff_reservation(&ticket, &list); - return r; + goto error; } amdgpu_vm_bo_del(adev, bo_va); - ttm_eu_backoff_reservation(&ticket, &list); - - return 0; +error: + drm_exec_fini(&exec); + return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 0593ef8fe0a6..e90f730eb715 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2850,10 +2850,7 @@ static const struct drm_driver amdgpu_kms_driver = { .show_fdinfo = amdgpu_show_fdinfo, #endif - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = amdgpu_gem_prime_import, - .gem_prime_mmap = drm_gem_prime_mmap, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -2877,10 +2874,7 @@ const struct drm_driver amdgpu_partition_driver = { .fops = &amdgpu_driver_kms_fops, .release = &amdgpu_driver_release_kms, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = amdgpu_gem_prime_import, - .gem_prime_mmap = drm_gem_prime_mmap, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 74055cba3dc9..7da871972a8e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -33,6 +33,7 @@ #include <drm/amdgpu_drm.h> #include <drm/drm_drv.h> +#include <drm/drm_exec.h> #include <drm/drm_gem_ttm_helper.h> #include <drm/ttm/ttm_tt.h> @@ -198,29 +199,24 @@ static void amdgpu_gem_object_close(struct drm_gem_object *obj, struct amdgpu_fpriv *fpriv = file_priv->driver_priv; struct amdgpu_vm *vm = &fpriv->vm; - struct amdgpu_bo_list_entry vm_pd; - struct list_head list, duplicates; struct dma_fence *fence = NULL; - struct ttm_validate_buffer tv; - struct ww_acquire_ctx ticket; struct amdgpu_bo_va *bo_va; + struct drm_exec exec; long r; - INIT_LIST_HEAD(&list); - INIT_LIST_HEAD(&duplicates); - - tv.bo = &bo->tbo; - tv.num_shared = 2; - list_add(&tv.head, &list); - - amdgpu_vm_get_pd_bo(vm, &list, &vm_pd); - - r = ttm_eu_reserve_buffers(&ticket, &list, false, &duplicates); - if (r) { - dev_err(adev->dev, "leaking bo va because " - "we fail to reserve bo (%ld)\n", r); - return; + drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES); + drm_exec_until_all_locked(&exec) { + r = drm_exec_prepare_obj(&exec, &bo->tbo.base, 1); + drm_exec_retry_on_contention(&exec); + if (unlikely(r)) + goto out_unlock; + + r = amdgpu_vm_lock_pd(vm, &exec, 0); + drm_exec_retry_on_contention(&exec); + if (unlikely(r)) + goto out_unlock; } + bo_va = amdgpu_vm_bo_find(vm, bo); if (!bo_va || --bo_va->ref_count) goto out_unlock; @@ -230,6 +226,9 @@ static void amdgpu_gem_object_close(struct drm_gem_object *obj, goto out_unlock; r = amdgpu_vm_clear_freed(adev, vm, &fence); + if (unlikely(r < 0)) + dev_err(adev->dev, "failed to clear page " + "tables on GEM object close (%ld)\n", r); if (r || !fence) goto out_unlock; @@ -237,10 +236,9 @@ static void amdgpu_gem_object_close(struct drm_gem_object *obj, dma_fence_put(fence); out_unlock: - if (unlikely(r < 0)) - dev_err(adev->dev, "failed to clear page " - "tables on GEM object close (%ld)\n", r); - ttm_eu_backoff_reservation(&ticket, &list); + if (r) + dev_err(adev->dev, "leaking bo va (%ld)\n", r); + drm_exec_fini(&exec); } static int amdgpu_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) @@ -675,10 +673,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, struct amdgpu_fpriv *fpriv = filp->driver_priv; struct amdgpu_bo *abo; struct amdgpu_bo_va *bo_va; - struct amdgpu_bo_list_entry vm_pd; - struct ttm_validate_buffer tv; - struct ww_acquire_ctx ticket; - struct list_head list, duplicates; + struct drm_exec exec; uint64_t va_flags; uint64_t vm_size; int r = 0; @@ -728,36 +723,38 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, return -EINVAL; } - INIT_LIST_HEAD(&list); - INIT_LIST_HEAD(&duplicates); if ((args->operation != AMDGPU_VA_OP_CLEAR) && !(args->flags & AMDGPU_VM_PAGE_PRT)) { gobj = drm_gem_object_lookup(filp, args->handle); if (gobj == NULL) return -ENOENT; abo = gem_to_amdgpu_bo(gobj); - tv.bo = &abo->tbo; - if (abo->flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID) - tv.num_shared = 1; - else - tv.num_shared = 0; - list_add(&tv.head, &list); } else { gobj = NULL; abo = NULL; } - amdgpu_vm_get_pd_bo(&fpriv->vm, &list, &vm_pd); + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT | + DRM_EXEC_IGNORE_DUPLICATES); + drm_exec_until_all_locked(&exec) { + if (gobj) { + r = drm_exec_lock_obj(&exec, gobj); + drm_exec_retry_on_contention(&exec); + if (unlikely(r)) + goto error; + } - r = ttm_eu_reserve_buffers(&ticket, &list, true, &duplicates); - if (r) - goto error_unref; + r = amdgpu_vm_lock_pd(&fpriv->vm, &exec, 2); + drm_exec_retry_on_contention(&exec); + if (unlikely(r)) + goto error; + } if (abo) { bo_va = amdgpu_vm_bo_find(&fpriv->vm, abo); if (!bo_va) { r = -ENOENT; - goto error_backoff; + goto error; } } else if (args->operation != AMDGPU_VA_OP_CLEAR) { bo_va = fpriv->prt_va; @@ -794,10 +791,8 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, amdgpu_gem_va_update_vm(adev, &fpriv->vm, bo_va, args->operation); -error_backoff: - ttm_eu_backoff_reservation(&ticket, &list); - -error_unref: +error: + drm_exec_fini(&exec); drm_gem_object_put(gobj); return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c index e9091ebfe230..3e3dadd6d0f3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c @@ -22,6 +22,7 @@ */ #include <linux/firmware.h> +#include <drm/drm_exec.h> #include "amdgpu_mes.h" #include "amdgpu.h" @@ -1168,34 +1169,31 @@ int amdgpu_mes_ctx_map_meta_data(struct amdgpu_device *adev, struct amdgpu_mes_ctx_data *ctx_data) { struct amdgpu_bo_va *bo_va; - struct ww_acquire_ctx ticket; - struct list_head list; - struct amdgpu_bo_list_entry pd; - struct ttm_validate_buffer csa_tv; struct amdgpu_sync sync; + struct drm_exec exec; int r; amdgpu_sync_create(&sync); - INIT_LIST_HEAD(&list); - INIT_LIST_HEAD(&csa_tv.head); - csa_tv.bo = &ctx_data->meta_data_obj->tbo; - csa_tv.num_shared = 1; - - list_add(&csa_tv.head, &list); - amdgpu_vm_get_pd_bo(vm, &list, &pd); - - r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); - if (r) { - DRM_ERROR("failed to reserve meta data BO: err=%d\n", r); - return r; + drm_exec_init(&exec, 0); + drm_exec_until_all_locked(&exec) { + r = drm_exec_lock_obj(&exec, + &ctx_data->meta_data_obj->tbo.base); + drm_exec_retry_on_contention(&exec); + if (unlikely(r)) + goto error_fini_exec; + + r = amdgpu_vm_lock_pd(vm, &exec, 0); + drm_exec_retry_on_contention(&exec); + if (unlikely(r)) + goto error_fini_exec; } bo_va = amdgpu_vm_bo_add(adev, vm, ctx_data->meta_data_obj); if (!bo_va) { - ttm_eu_backoff_reservation(&ticket, &list); DRM_ERROR("failed to create bo_va for meta data BO\n"); - return -ENOMEM; + r = -ENOMEM; + goto error_fini_exec; } r = amdgpu_vm_bo_map(adev, bo_va, ctx_data->meta_data_gpu_addr, 0, @@ -1205,33 +1203,35 @@ int amdgpu_mes_ctx_map_meta_data(struct amdgpu_device *adev, if (r) { DRM_ERROR("failed to do bo_map on meta data, err=%d\n", r); - goto error; + goto error_del_bo_va; } r = amdgpu_vm_bo_update(adev, bo_va, false); if (r) { DRM_ERROR("failed to do vm_bo_update on meta data\n"); - goto error; + goto error_del_bo_va; } amdgpu_sync_fence(&sync, bo_va->last_pt_update); r = amdgpu_vm_update_pdes(adev, vm, false); if (r) { DRM_ERROR("failed to update pdes on meta data\n"); - goto error; + goto error_del_bo_va; } amdgpu_sync_fence(&sync, vm->last_update); amdgpu_sync_wait(&sync, false); - ttm_eu_backoff_reservation(&ticket, &list); + drm_exec_fini(&exec); amdgpu_sync_free(&sync); ctx_data->meta_data_va = bo_va; return 0; -error: +error_del_bo_va: amdgpu_vm_bo_del(adev, bo_va); - ttm_eu_backoff_reservation(&ticket, &list); + +error_fini_exec: + drm_exec_fini(&exec); amdgpu_sync_free(&sync); return r; } @@ -1242,34 +1242,30 @@ int amdgpu_mes_ctx_unmap_meta_data(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va = ctx_data->meta_data_va; struct amdgpu_bo *bo = ctx_data->meta_data_obj; struct amdgpu_vm *vm = bo_va->base.vm; - struct amdgpu_bo_list_entry vm_pd; - struct list_head list, duplicates; - struct dma_fence *fence = NULL; - struct ttm_validate_buffer tv; - struct ww_acquire_ctx ticket; - long r = 0; - - INIT_LIST_HEAD(&list); - INIT_LIST_HEAD(&duplicates); - - tv.bo = &bo->tbo; - tv.num_shared = 2; - list_add(&tv.head, &list); - - amdgpu_vm_get_pd_bo(vm, &list, &vm_pd); - - r = ttm_eu_reserve_buffers(&ticket, &list, false, &duplicates); - if (r) { - dev_err(adev->dev, "leaking bo va because " - "we fail to reserve bo (%ld)\n", r); - return r; + struct dma_fence *fence; + struct drm_exec exec; + long r; + + drm_exec_init(&exec, 0); + drm_exec_until_all_locked(&exec) { + r = drm_exec_lock_obj(&exec, + &ctx_data->meta_data_obj->tbo.base); + drm_exec_retry_on_contention(&exec); + if (unlikely(r)) + goto out_unlock; + + r = amdgpu_vm_lock_pd(vm, &exec, 0); + drm_exec_retry_on_contention(&exec); + if (unlikely(r)) + goto out_unlock; } amdgpu_vm_bo_del(adev, bo_va); if (!amdgpu_vm_ready(vm)) goto out_unlock; - r = dma_resv_get_singleton(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, &fence); + r = dma_resv_get_singleton(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, + &fence); if (r) goto out_unlock; if (fence) { @@ -1288,7 +1284,7 @@ int amdgpu_mes_ctx_unmap_meta_data(struct amdgpu_device *adev, out_unlock: if (unlikely(r < 0)) dev_err(adev->dev, "failed to clear page tables (%ld)\n", r); - ttm_eu_backoff_reservation(&ticket, &list); + drm_exec_fini(&exec); return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 291977b93b1d..d0e6009a985b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -34,6 +34,7 @@ #include <drm/amdgpu_drm.h> #include <drm/drm_drv.h> #include <drm/ttm/ttm_tt.h> +#include <drm/drm_exec.h> #include "amdgpu.h" #include "amdgpu_trace.h" #include "amdgpu_amdkfd.h" @@ -339,25 +340,20 @@ void amdgpu_vm_bo_base_init(struct amdgpu_vm_bo_base *base, } /** - * amdgpu_vm_get_pd_bo - add the VM PD to a validation list + * amdgpu_vm_lock_pd - lock PD in drm_exec * * @vm: vm providing the BOs - * @validated: head of validation list - * @entry: entry to add + * @exec: drm execution context + * @num_fences: number of extra fences to reserve * - * Add the page directory to the list of BOs to - * validate for command submission. + * Lock the VM root PD in the DRM execution context. */ -void amdgpu_vm_get_pd_bo(struct amdgpu_vm *vm, - struct list_head *validated, - struct amdgpu_bo_list_entry *entry) +int amdgpu_vm_lock_pd(struct amdgpu_vm *vm, struct drm_exec *exec, + unsigned int num_fences) { - entry->priority = 0; - entry->tv.bo = &vm->root.bo->tbo; - /* Two for VM updates, one for TTM and one for the CS job */ - entry->tv.num_shared = 4; - entry->user_pages = NULL; - list_add(&entry->tv.head, validated); + /* We need at least two fences for the VM PD/PT updates */ + return drm_exec_prepare_obj(exec, &vm->root.bo->tbo.base, + 2 + num_fences); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 9c85d494f2a2..45bcf46c6f59 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -36,6 +36,8 @@ #include "amdgpu_ring.h" #include "amdgpu_ids.h" +struct drm_exec; + struct amdgpu_bo_va; struct amdgpu_job; struct amdgpu_bo_list_entry; @@ -396,9 +398,8 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm); int amdgpu_vm_make_compute(struct amdgpu_device *adev, struct amdgpu_vm *vm); void amdgpu_vm_release_compute(struct amdgpu_device *adev, struct amdgpu_vm *vm); void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm); -void amdgpu_vm_get_pd_bo(struct amdgpu_vm *vm, - struct list_head *validated, - struct amdgpu_bo_list_entry *entry); +int amdgpu_vm_lock_pd(struct amdgpu_vm *vm, struct drm_exec *exec, + unsigned int num_fences); bool amdgpu_vm_ready(struct amdgpu_vm *vm); uint64_t amdgpu_vm_generation(struct amdgpu_device *adev, struct amdgpu_vm *vm); int amdgpu_vm_validate_pt_bos(struct amdgpu_device *adev, struct amdgpu_vm *vm, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 5ff1a5a89d96..aa32b06eea77 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -24,6 +24,8 @@ #include <linux/types.h> #include <linux/sched/task.h> #include <drm/ttm/ttm_tt.h> +#include <drm/drm_exec.h> + #include "amdgpu_sync.h" #include "amdgpu_object.h" #include "amdgpu_vm.h" @@ -1455,37 +1457,34 @@ struct svm_validate_context { struct svm_range *prange; bool intr; DECLARE_BITMAP(bitmap, MAX_GPU_INSTANCE); - struct ttm_validate_buffer tv[MAX_GPU_INSTANCE]; - struct list_head validate_list; - struct ww_acquire_ctx ticket; + struct drm_exec exec; }; -static int svm_range_reserve_bos(struct svm_validate_context *ctx) +static int svm_range_reserve_bos(struct svm_validate_context *ctx, bool intr) { struct kfd_process_device *pdd; struct amdgpu_vm *vm; uint32_t gpuidx; int r; - INIT_LIST_HEAD(&ctx->validate_list); - for_each_set_bit(gpuidx, ctx->bitmap, MAX_GPU_INSTANCE) { - pdd = kfd_process_device_from_gpuidx(ctx->process, gpuidx); - if (!pdd) { - pr_debug("failed to find device idx %d\n", gpuidx); - return -EINVAL; - } - vm = drm_priv_to_vm(pdd->drm_priv); - - ctx->tv[gpuidx].bo = &vm->root.bo->tbo; - ctx->tv[gpuidx].num_shared = 4; - list_add(&ctx->tv[gpuidx].head, &ctx->validate_list); - } + drm_exec_init(&ctx->exec, intr ? DRM_EXEC_INTERRUPTIBLE_WAIT: 0); + drm_exec_until_all_locked(&ctx->exec) { + for_each_set_bit(gpuidx, ctx->bitmap, MAX_GPU_INSTANCE) { + pdd = kfd_process_device_from_gpuidx(ctx->process, gpuidx); + if (!pdd) { + pr_debug("failed to find device idx %d\n", gpuidx); + r = -EINVAL; + goto unreserve_out; + } + vm = drm_priv_to_vm(pdd->drm_priv); - r = ttm_eu_reserve_buffers(&ctx->ticket, &ctx->validate_list, - ctx->intr, NULL); - if (r) { - pr_debug("failed %d to reserve bo\n", r); - return r; + r = amdgpu_vm_lock_pd(vm, &ctx->exec, 2); + drm_exec_retry_on_contention(&ctx->exec); + if (unlikely(r)) { + pr_debug("failed %d to reserve bo\n", r); + goto unreserve_out; + } + } } for_each_set_bit(gpuidx, ctx->bitmap, MAX_GPU_INSTANCE) { @@ -1508,13 +1507,13 @@ static int svm_range_reserve_bos(struct svm_validate_context *ctx) return 0; unreserve_out: - ttm_eu_backoff_reservation(&ctx->ticket, &ctx->validate_list); + drm_exec_fini(&ctx->exec); return r; } static void svm_range_unreserve_bos(struct svm_validate_context *ctx) { - ttm_eu_backoff_reservation(&ctx->ticket, &ctx->validate_list); + drm_exec_fini(&ctx->exec); } static void *kfd_svm_page_owner(struct kfd_process *p, int32_t gpuidx) @@ -1613,7 +1612,7 @@ static int svm_range_validate_and_map(struct mm_struct *mm, goto free_ctx; } - svm_range_reserve_bos(ctx); + svm_range_reserve_bos(ctx, intr); p = container_of(prange->svms, struct kfd_process, svms); owner = kfd_svm_page_owner(p, find_first_bit(ctx->bitmap, diff --git a/drivers/gpu/drm/arm/Kconfig b/drivers/gpu/drm/arm/Kconfig index c1b89274d2a4..ddf20708370f 100644 --- a/drivers/gpu/drm/arm/Kconfig +++ b/drivers/gpu/drm/arm/Kconfig @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 menu "ARM devices" + depends on DRM config DRM_HDLCD tristate "ARM HDLCD" diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c index 28f76e07dd95..c597c362f689 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c @@ -131,10 +131,9 @@ static int komeda_platform_probe(struct platform_device *pdev) return component_master_add_with_match(dev, &komeda_master_ops, match); } -static int komeda_platform_remove(struct platform_device *pdev) +static void komeda_platform_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &komeda_master_ops); - return 0; } static const struct of_device_id komeda_of_match[] = { @@ -189,7 +188,7 @@ static const struct dev_pm_ops komeda_pm_ops = { static struct platform_driver komeda_platform_driver = { .probe = komeda_platform_probe, - .remove = komeda_platform_remove, + .remove_new = komeda_platform_remove, .driver = { .name = "komeda", .of_match_table = komeda_of_match, diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 12f5a2c7f03d..aa06f9838015 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -367,10 +367,9 @@ static int hdlcd_probe(struct platform_device *pdev) match); } -static int hdlcd_remove(struct platform_device *pdev) +static void hdlcd_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &hdlcd_master_ops); - return 0; } static const struct of_device_id hdlcd_of_match[] = { @@ -399,7 +398,7 @@ static SIMPLE_DEV_PM_OPS(hdlcd_pm_ops, hdlcd_pm_suspend, hdlcd_pm_resume); static struct platform_driver hdlcd_platform_driver = { .probe = hdlcd_probe, - .remove = hdlcd_remove, + .remove_new = hdlcd_remove, .driver = { .name = "hdlcd", .pm = &hdlcd_pm_ops, diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index c03cfd57b752..46ff3521570c 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -935,10 +935,9 @@ static int malidp_platform_probe(struct platform_device *pdev) match); } -static int malidp_platform_remove(struct platform_device *pdev) +static void malidp_platform_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &malidp_master_ops); - return 0; } static int __maybe_unused malidp_pm_suspend(struct device *dev) @@ -981,7 +980,7 @@ static const struct dev_pm_ops malidp_pm_ops = { static struct platform_driver malidp_platform_driver = { .probe = malidp_platform_probe, - .remove = malidp_platform_remove, + .remove_new = malidp_platform_remove, .driver = { .name = "mali-dp", .pm = &malidp_pm_ops, diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index e120144d4b47..e8d2fe955909 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -37,8 +37,6 @@ static const struct drm_ioctl_desc armada_ioctls[] = { DEFINE_DRM_GEM_FOPS(armada_drm_fops); static const struct drm_driver armada_drm_driver = { - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = armada_gem_prime_import, .dumb_create = armada_gem_dumb_create, .major = 1, diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c index c8c7f8215155..d207b03f8357 100644 --- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c @@ -351,20 +351,18 @@ err_unload: return ret; } -static int aspeed_gfx_remove(struct platform_device *pdev) +static void aspeed_gfx_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); sysfs_remove_group(&pdev->dev.kobj, &aspeed_sysfs_attr_group); drm_dev_unregister(drm); aspeed_gfx_unload(drm); - - return 0; } static struct platform_driver aspeed_gfx_platform_driver = { .probe = aspeed_gfx_probe, - .remove = aspeed_gfx_remove, + .remove_new = aspeed_gfx_remove, .driver = { .name = "aspeed_gfx", .of_match_table = aspeed_gfx_match, diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c index 1bc35a992369..a5d285850ffb 100644 --- a/drivers/gpu/drm/ast/ast_dp501.c +++ b/drivers/gpu/drm/ast/ast_dp501.c @@ -350,7 +350,7 @@ static bool ast_init_dvo(struct drm_device *dev) data |= 0x00000500; ast_write32(ast, 0x12008, data); - if (ast->chip == AST2300) { + if (IS_AST_GEN4(ast)) { data = ast_read32(ast, 0x12084); /* multi-pins for DVO single-edge */ data |= 0xfffe0000; @@ -366,7 +366,7 @@ static bool ast_init_dvo(struct drm_device *dev) data &= 0xffffffcf; data |= 0x00000020; ast_write32(ast, 0x12090, data); - } else { /* AST2400 */ + } else { /* AST GEN5+ */ data = ast_read32(ast, 0x12088); /* multi-pins for DVO single-edge */ data |= 0x30000000; @@ -437,7 +437,7 @@ void ast_init_3rdtx(struct drm_device *dev) struct ast_device *ast = to_ast_device(dev); u8 jreg; - if (ast->chip == AST2300 || ast->chip == AST2400) { + if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) { jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); switch (jreg & 0x0e) { case 0x04: diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 5498a6676f2e..3f6e0c984523 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -52,19 +52,38 @@ #define PCI_CHIP_AST2000 0x2000 #define PCI_CHIP_AST2100 0x2010 +#define __AST_CHIP(__gen, __index) ((__gen) << 16 | (__index)) enum ast_chip { - AST2000, - AST2100, - AST1100, - AST2200, - AST2150, - AST2300, - AST2400, - AST2500, - AST2600, + /* 1st gen */ + AST1000 = __AST_CHIP(1, 0), // unused + AST2000 = __AST_CHIP(1, 1), + /* 2nd gen */ + AST1100 = __AST_CHIP(2, 0), + AST2100 = __AST_CHIP(2, 1), + AST2050 = __AST_CHIP(2, 2), // unused + /* 3rd gen */ + AST2200 = __AST_CHIP(3, 0), + AST2150 = __AST_CHIP(3, 1), + /* 4th gen */ + AST2300 = __AST_CHIP(4, 0), + AST1300 = __AST_CHIP(4, 1), + AST1050 = __AST_CHIP(4, 2), // unused + /* 5th gen */ + AST2400 = __AST_CHIP(5, 0), + AST1400 = __AST_CHIP(5, 1), + AST1250 = __AST_CHIP(5, 2), // unused + /* 6th gen */ + AST2500 = __AST_CHIP(6, 0), + AST2510 = __AST_CHIP(6, 1), + AST2520 = __AST_CHIP(6, 2), // unused + /* 7th gen */ + AST2600 = __AST_CHIP(7, 0), + AST2620 = __AST_CHIP(7, 1), // unused }; +#define __AST_CHIP_GEN(__chip) (((unsigned long)(__chip)) >> 16) + enum ast_tx_chip { AST_TX_NONE, AST_TX_SIL164, @@ -166,7 +185,6 @@ struct ast_device { void __iomem *dp501_fw_buf; enum ast_chip chip; - bool vga2_clone; uint32_t dram_bus_width; uint32_t dram_type; uint32_t mclk; @@ -219,6 +237,24 @@ struct ast_device *ast_device_create(const struct drm_driver *drv, struct pci_dev *pdev, unsigned long flags); +static inline unsigned long __ast_gen(struct ast_device *ast) +{ + return __AST_CHIP_GEN(ast->chip); +} +#define AST_GEN(__ast) __ast_gen(__ast) + +static inline bool __ast_gen_is_eq(struct ast_device *ast, unsigned long gen) +{ + return __ast_gen(ast) == gen; +} +#define IS_AST_GEN1(__ast) __ast_gen_is_eq(__ast, 1) +#define IS_AST_GEN2(__ast) __ast_gen_is_eq(__ast, 2) +#define IS_AST_GEN3(__ast) __ast_gen_is_eq(__ast, 3) +#define IS_AST_GEN4(__ast) __ast_gen_is_eq(__ast, 4) +#define IS_AST_GEN5(__ast) __ast_gen_is_eq(__ast, 5) +#define IS_AST_GEN6(__ast) __ast_gen_is_eq(__ast, 6) +#define IS_AST_GEN7(__ast) __ast_gen_is_eq(__ast, 7) + #define AST_IO_AR_PORT_WRITE (0x40) #define AST_IO_MISC_PORT_WRITE (0x42) #define AST_IO_VGA_ENABLE_PORT (0x43) @@ -258,26 +294,35 @@ static inline void ast_io_write8(struct ast_device *ast, u32 reg, u8 val) iowrite8(val, ast->ioregs + reg); } -static inline void ast_set_index_reg(struct ast_device *ast, - uint32_t base, uint8_t index, - uint8_t val) +static inline u8 ast_get_index_reg(struct ast_device *ast, u32 base, u8 index) { ast_io_write8(ast, base, index); ++base; - ast_io_write8(ast, base, val); + return ast_io_read8(ast, base); +} + +static inline u8 ast_get_index_reg_mask(struct ast_device *ast, u32 base, u8 index, + u8 preserve_mask) +{ + u8 val = ast_get_index_reg(ast, base, index); + + return val & preserve_mask; } -void ast_set_index_reg_mask(struct ast_device *ast, - uint32_t base, uint8_t index, - uint8_t mask, uint8_t val); -uint8_t ast_get_index_reg(struct ast_device *ast, - uint32_t base, uint8_t index); -uint8_t ast_get_index_reg_mask(struct ast_device *ast, - uint32_t base, uint8_t index, uint8_t mask); +static inline void ast_set_index_reg(struct ast_device *ast, u32 base, u8 index, u8 val) +{ + ast_io_write8(ast, base, index); + ++base; + ast_io_write8(ast, base, val); +} -static inline void ast_open_key(struct ast_device *ast) +static inline void ast_set_index_reg_mask(struct ast_device *ast, u32 base, u8 index, + u8 preserve_mask, u8 val) { - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x80, 0xA8); + u8 tmp = ast_get_index_reg_mask(ast, base, index, preserve_mask); + + tmp |= val; + ast_set_index_reg(ast, base, index, tmp); } #define AST_VIDMEM_SIZE_8M 0x00800000 @@ -458,9 +503,6 @@ int ast_mode_config_init(struct ast_device *ast); int ast_mm_init(struct ast_device *ast); /* ast post */ -void ast_enable_vga(struct drm_device *dev); -void ast_enable_mmio(struct drm_device *dev); -bool ast_is_vga_enabled(struct drm_device *dev); void ast_post_gpu(struct drm_device *dev); u32 ast_mindwm(struct ast_device *ast, u32 r); void ast_moutdwm(struct ast_device *ast, u32 r, u32 v); diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 1f35438f614a..8bfbdfd86d77 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -35,131 +35,170 @@ #include "ast_drv.h" -void ast_set_index_reg_mask(struct ast_device *ast, - uint32_t base, uint8_t index, - uint8_t mask, uint8_t val) +static int ast_init_pci_config(struct pci_dev *pdev) { - u8 tmp; - ast_io_write8(ast, base, index); - tmp = (ast_io_read8(ast, base + 1) & mask) | val; - ast_set_index_reg(ast, base, index, tmp); + int err; + u16 pcis04; + + err = pci_read_config_word(pdev, PCI_COMMAND, &pcis04); + if (err) + goto out; + + pcis04 |= PCI_COMMAND_MEMORY | PCI_COMMAND_IO; + + err = pci_write_config_word(pdev, PCI_COMMAND, pcis04); + +out: + return pcibios_err_to_errno(err); } -uint8_t ast_get_index_reg(struct ast_device *ast, - uint32_t base, uint8_t index) +static bool ast_is_vga_enabled(struct drm_device *dev) { - uint8_t ret; - ast_io_write8(ast, base, index); - ret = ast_io_read8(ast, base + 1); - return ret; + struct ast_device *ast = to_ast_device(dev); + u8 ch; + + ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT); + + return !!(ch & 0x01); } -uint8_t ast_get_index_reg_mask(struct ast_device *ast, - uint32_t base, uint8_t index, uint8_t mask) +static void ast_enable_vga(struct drm_device *dev) { - uint8_t ret; - ast_io_write8(ast, base, index); - ret = ast_io_read8(ast, base + 1) & mask; - return ret; + struct ast_device *ast = to_ast_device(dev); + + ast_io_write8(ast, AST_IO_VGA_ENABLE_PORT, 0x01); + ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, 0x01); } -static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) +/* + * Run this function as part of the HW device cleanup; not + * when the DRM device gets released. + */ +static void ast_enable_mmio_release(void *data) { - struct device_node *np = dev->dev->of_node; - struct ast_device *ast = to_ast_device(dev); - struct pci_dev *pdev = to_pci_dev(dev->dev); - uint32_t data, jregd0, jregd1; + struct ast_device *ast = data; - /* Defaults */ - ast->config_mode = ast_use_defaults; - *scu_rev = 0xffffffff; + /* enable standard VGA decode */ + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x04); +} - /* Check if we have device-tree properties */ - if (np && !of_property_read_u32(np, "aspeed,scu-revision-id", - scu_rev)) { - /* We do, disable P2A access */ - ast->config_mode = ast_use_dt; - drm_info(dev, "Using device-tree for configuration\n"); - return; - } +static int ast_enable_mmio(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; - /* Not all families have a P2A bridge */ - if (pdev->device != PCI_CHIP_AST2000) - return; + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x06); - /* - * The BMC will set SCU 0x40 D[12] to 1 if the P2 bridge - * is disabled. We force using P2A if VGA only mode bit - * is set D[7] - */ - jregd0 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); - jregd1 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); - if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { - /* Patch AST2500 */ - if (((pdev->revision & 0xF0) == 0x40) - && ((jregd0 & AST_VRAM_INIT_STATUS_MASK) == 0)) - ast_patch_ahb_2500(ast); - - /* Double check it's actually working */ - data = ast_read32(ast, 0xf004); - if ((data != 0xFFFFFFFF) && (data != 0x00)) { - /* P2A works, grab silicon revision */ - ast->config_mode = ast_use_p2a; - - drm_info(dev, "Using P2A bridge for configuration\n"); - - /* Read SCU7c (silicon revision register) */ - ast_write32(ast, 0xf004, 0x1e6e0000); - ast_write32(ast, 0xf000, 0x1); - *scu_rev = ast_read32(ast, 0x1207c); - return; - } - } + return devm_add_action_or_reset(dev->dev, ast_enable_mmio_release, ast); +} - /* We have a P2A bridge but it's disabled */ - drm_info(dev, "P2A bridge disabled, using default configuration\n"); +static void ast_open_key(struct ast_device *ast) +{ + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x80, 0xA8); } -static int ast_detect_chip(struct drm_device *dev, bool *need_post) +static int ast_device_config_init(struct ast_device *ast) { - struct ast_device *ast = to_ast_device(dev); + struct drm_device *dev = &ast->base; struct pci_dev *pdev = to_pci_dev(dev->dev); - uint32_t jreg, scu_rev; + struct device_node *np = dev->dev->of_node; + uint32_t scu_rev = 0xffffffff; + u32 data; + u8 jregd0, jregd1; /* - * If VGA isn't enabled, we need to enable now or subsequent - * access to the scratch registers will fail. We also inform - * our caller that it needs to POST the chip - * (Assumption: VGA not enabled -> need to POST) + * Find configuration mode and read SCU revision */ - if (!ast_is_vga_enabled(dev)) { - ast_enable_vga(dev); - drm_info(dev, "VGA not enabled on entry, requesting chip POST\n"); - *need_post = true; - } else - *need_post = false; + ast->config_mode = ast_use_defaults; - /* Enable extended register access */ - ast_open_key(ast); - ast_enable_mmio(dev); + /* Check if we have device-tree properties */ + if (np && !of_property_read_u32(np, "aspeed,scu-revision-id", &data)) { + /* We do, disable P2A access */ + ast->config_mode = ast_use_dt; + scu_rev = data; + } else if (pdev->device == PCI_CHIP_AST2000) { // Not all families have a P2A bridge + /* + * The BMC will set SCU 0x40 D[12] to 1 if the P2 bridge + * is disabled. We force using P2A if VGA only mode bit + * is set D[7] + */ + jregd0 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + jregd1 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); + if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { + + /* + * We have a P2A bridge and it is enabled. + */ + + /* Patch AST2500/AST2510 */ + if ((pdev->revision & 0xf0) == 0x40) { + if (!(jregd0 & AST_VRAM_INIT_STATUS_MASK)) + ast_patch_ahb_2500(ast); + } + + /* Double check that it's actually working */ + data = ast_read32(ast, 0xf004); + if ((data != 0xffffffff) && (data != 0x00)) { + ast->config_mode = ast_use_p2a; + + /* Read SCU7c (silicon revision register) */ + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + scu_rev = ast_read32(ast, 0x1207c); + } + } + } + + switch (ast->config_mode) { + case ast_use_defaults: + drm_info(dev, "Using default configuration\n"); + break; + case ast_use_dt: + drm_info(dev, "Using device-tree for configuration\n"); + break; + case ast_use_p2a: + drm_info(dev, "Using P2A bridge for configuration\n"); + break; + } - /* Find out whether P2A works or whether to use device-tree */ - ast_detect_config_mode(dev, &scu_rev); + /* + * Identify chipset + */ - /* Identify chipset */ if (pdev->revision >= 0x50) { ast->chip = AST2600; drm_info(dev, "AST 2600 detected\n"); } else if (pdev->revision >= 0x40) { - ast->chip = AST2500; - drm_info(dev, "AST 2500 detected\n"); + switch (scu_rev & 0x300) { + case 0x0100: + ast->chip = AST2510; + drm_info(dev, "AST 2510 detected\n"); + break; + default: + ast->chip = AST2500; + drm_info(dev, "AST 2500 detected\n"); + } } else if (pdev->revision >= 0x30) { - ast->chip = AST2400; - drm_info(dev, "AST 2400 detected\n"); + switch (scu_rev & 0x300) { + case 0x0100: + ast->chip = AST1400; + drm_info(dev, "AST 1400 detected\n"); + break; + default: + ast->chip = AST2400; + drm_info(dev, "AST 2400 detected\n"); + } } else if (pdev->revision >= 0x20) { - ast->chip = AST2300; - drm_info(dev, "AST 2300 detected\n"); + switch (scu_rev & 0x300) { + case 0x0000: + ast->chip = AST1300; + drm_info(dev, "AST 1300 detected\n"); + break; + default: + ast->chip = AST2300; + drm_info(dev, "AST 2300 detected\n"); + break; + } } else if (pdev->revision >= 0x10) { switch (scu_rev & 0x0300) { case 0x0200: @@ -179,15 +218,21 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) drm_info(dev, "AST 2100 detected\n"); break; } - ast->vga2_clone = false; } else { ast->chip = AST2000; drm_info(dev, "AST 2000 detected\n"); } + return 0; +} + +static void ast_detect_widescreen(struct ast_device *ast) +{ + u8 jreg; + /* Check if we support wide screen */ - switch (ast->chip) { - case AST2000: + switch (AST_GEN(ast)) { + case 1: ast->support_wide_screen = false; break; default: @@ -198,20 +243,23 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) ast->support_wide_screen = true; else { ast->support_wide_screen = false; - if (ast->chip == AST2300 && - (scu_rev & 0x300) == 0x0) /* ast1300 */ + if (ast->chip == AST1300) ast->support_wide_screen = true; - if (ast->chip == AST2400 && - (scu_rev & 0x300) == 0x100) /* ast1400 */ + if (ast->chip == AST1400) ast->support_wide_screen = true; - if (ast->chip == AST2500 && - scu_rev == 0x100) /* ast2510 */ + if (ast->chip == AST2510) ast->support_wide_screen = true; - if (ast->chip == AST2600) /* ast2600 */ + if (IS_AST_GEN7(ast)) ast->support_wide_screen = true; } break; } +} + +static void ast_detect_tx_chip(struct ast_device *ast, bool need_post) +{ + struct drm_device *dev = &ast->base; + u8 jreg; /* Check 3rd Tx option (digital output afaik) */ ast->tx_chip_types |= AST_TX_NONE_BIT; @@ -224,15 +272,15 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) * is at power-on reset, otherwise we'll incorrectly "detect" a * SIL164 when there is none. */ - if (!*need_post) { + if (!need_post) { jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff); if (jreg & 0x80) ast->tx_chip_types = AST_TX_SIL164_BIT; } - if ((ast->chip == AST2300) || (ast->chip == AST2400) || (ast->chip == AST2500)) { + if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast) || IS_AST_GEN6(ast)) { /* - * On AST2300 and 2400, look the configuration set by the SoC in + * On AST GEN4+, look the configuration set by the SoC in * the SOC scratch register #1 bits 11:8 (interestingly marked * as "reserved" in the spec) */ @@ -254,7 +302,7 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) case 0x0c: ast->tx_chip_types = AST_TX_DP501_BIT; } - } else if (ast->chip == AST2600) { + } else if (IS_AST_GEN7(ast)) { if (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, TX_TYPE_MASK) == ASTDP_DPMCU_TX) { ast->tx_chip_types = AST_TX_ASTDP_BIT; @@ -271,8 +319,6 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) drm_info(dev, "Using DP501 DisplayPort transmitter\n"); if (ast->tx_chip_types & AST_TX_ASTDP_BIT) drm_info(dev, "Using ASPEED DisplayPort transmitter\n"); - - return 0; } static int ast_get_dram_info(struct drm_device *dev) @@ -286,7 +332,7 @@ static int ast_get_dram_info(struct drm_device *dev) case ast_use_dt: /* * If some properties are missing, use reasonable - * defaults for AST2400 + * defaults for GEN5 */ if (of_property_read_u32(np, "aspeed,mcr-configuration", &mcr_cfg)) @@ -309,7 +355,7 @@ static int ast_get_dram_info(struct drm_device *dev) default: ast->dram_bus_width = 16; ast->dram_type = AST_DRAM_1Gx16; - if (ast->chip == AST2500) + if (IS_AST_GEN6(ast)) ast->mclk = 800; else ast->mclk = 396; @@ -321,7 +367,7 @@ static int ast_get_dram_info(struct drm_device *dev) else ast->dram_bus_width = 32; - if (ast->chip == AST2500) { + if (IS_AST_GEN6(ast)) { switch (mcr_cfg & 0x03) { case 0: ast->dram_type = AST_DRAM_1Gx16; @@ -337,7 +383,7 @@ static int ast_get_dram_info(struct drm_device *dev) ast->dram_type = AST_DRAM_8Gx16; break; } - } else if (ast->chip == AST2300 || ast->chip == AST2400) { + } else if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) { switch (mcr_cfg & 0x03) { case 0: ast->dram_type = AST_DRAM_512Mx16; @@ -395,25 +441,13 @@ static int ast_get_dram_info(struct drm_device *dev) return 0; } -/* - * Run this function as part of the HW device cleanup; not - * when the DRM device gets released. - */ -static void ast_device_release(void *data) -{ - struct ast_device *ast = data; - - /* enable standard VGA decode */ - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x04); -} - struct ast_device *ast_device_create(const struct drm_driver *drv, struct pci_dev *pdev, unsigned long flags) { struct drm_device *dev; struct ast_device *ast; - bool need_post; + bool need_post = false; int ret = 0; ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); @@ -449,7 +483,34 @@ struct ast_device *ast_device_create(const struct drm_driver *drv, return ERR_PTR(-EIO); } - ast_detect_chip(dev, &need_post); + ret = ast_init_pci_config(pdev); + if (ret) + return ERR_PTR(ret); + + if (!ast_is_vga_enabled(dev)) { + drm_info(dev, "VGA not enabled on entry, requesting chip POST\n"); + need_post = true; + } + + /* + * If VGA isn't enabled, we need to enable now or subsequent + * access to the scratch registers will fail. + */ + if (need_post) + ast_enable_vga(dev); + + /* Enable extended register access */ + ast_open_key(ast); + ret = ast_enable_mmio(ast); + if (ret) + return ERR_PTR(ret); + + ret = ast_device_config_init(ast); + if (ret) + return ERR_PTR(ret); + + ast_detect_widescreen(ast); + ast_detect_tx_chip(ast, need_post); ret = ast_get_dram_info(dev); if (ret) @@ -477,9 +538,5 @@ struct ast_device *ast_device_create(const struct drm_driver *drv, if (ret) return ERR_PTR(ret); - ret = devm_add_action_or_reset(dev->dev, ast_device_release, ast); - if (ret) - return ERR_PTR(ret); - return ast; } diff --git a/drivers/gpu/drm/ast/ast_mm.c b/drivers/gpu/drm/ast/ast_mm.c index e16af60deef9..bc174bd933b9 100644 --- a/drivers/gpu/drm/ast/ast_mm.c +++ b/drivers/gpu/drm/ast/ast_mm.c @@ -38,8 +38,6 @@ static u32 ast_get_vram_size(struct ast_device *ast) u8 jreg; u32 vram_size; - ast_open_key(ast); - vram_size = AST_VIDMEM_DEFAULT_SIZE; jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff); switch (jreg & 3) { diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index b3c670af6ef2..f711d592da52 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -342,7 +342,7 @@ static void ast_set_crtc_reg(struct ast_device *ast, u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0; u16 temp, precache = 0; - if ((ast->chip == AST2500 || ast->chip == AST2600) && + if ((IS_AST_GEN6(ast) || IS_AST_GEN7(ast)) && (vbios_mode->enh_table->flags & AST2500PreCatchCRT)) precache = 40; @@ -384,7 +384,7 @@ static void ast_set_crtc_reg(struct ast_device *ast, ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAD, 0x00, jregAD); // Workaround for HSync Time non octave pixels (1920x1080@60Hz HSync 44 pixels); - if ((ast->chip == AST2600) && (mode->crtc_vdisplay == 1080)) + if (IS_AST_GEN7(ast) && (mode->crtc_vdisplay == 1080)) ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xFC, 0xFD, 0x02); else ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xFC, 0xFD, 0x00); @@ -466,7 +466,7 @@ static void ast_set_dclk_reg(struct ast_device *ast, { const struct ast_vbios_dclk_info *clk_info; - if ((ast->chip == AST2500) || (ast->chip == AST2600)) + if (IS_AST_GEN6(ast) || IS_AST_GEN7(ast)) clk_info = &dclk_table_ast2500[vbios_mode->enh_table->dclk_index]; else clk_info = &dclk_table[vbios_mode->enh_table->dclk_index]; @@ -510,17 +510,13 @@ static void ast_set_color_reg(struct ast_device *ast, static void ast_set_crtthd_reg(struct ast_device *ast) { /* Set Threshold */ - if (ast->chip == AST2600) { + if (IS_AST_GEN7(ast)) { ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0xe0); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0xa0); - } else if (ast->chip == AST2300 || ast->chip == AST2400 || - ast->chip == AST2500) { + } else if (IS_AST_GEN6(ast) || IS_AST_GEN5(ast) || IS_AST_GEN4(ast)) { ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x78); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x60); - } else if (ast->chip == AST2100 || - ast->chip == AST1100 || - ast->chip == AST2200 || - ast->chip == AST2150) { + } else if (IS_AST_GEN3(ast) || IS_AST_GEN2(ast)) { ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x3f); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x2f); } else { @@ -1082,9 +1078,10 @@ ast_crtc_helper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode if ((mode->hdisplay == 1152) && (mode->vdisplay == 864)) return MODE_OK; - if ((ast->chip == AST2100) || (ast->chip == AST2200) || - (ast->chip == AST2300) || (ast->chip == AST2400) || - (ast->chip == AST2500) || (ast->chip == AST2600)) { + if ((ast->chip == AST2100) || // GEN2, but not AST1100 (?) + (ast->chip == AST2200) || // GEN3, but not AST2150 (?) + IS_AST_GEN4(ast) || IS_AST_GEN5(ast) || + IS_AST_GEN6(ast) || IS_AST_GEN7(ast)) { if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080)) return MODE_OK; @@ -1800,12 +1797,12 @@ int ast_mode_config_init(struct ast_device *ast) dev->mode_config.min_height = 0; dev->mode_config.preferred_depth = 24; - if (ast->chip == AST2100 || - ast->chip == AST2200 || - ast->chip == AST2300 || - ast->chip == AST2400 || - ast->chip == AST2500 || - ast->chip == AST2600) { + if (ast->chip == AST2100 || // GEN2, but not AST1100 (?) + ast->chip == AST2200 || // GEN3, but not AST2150 (?) + IS_AST_GEN7(ast) || + IS_AST_GEN6(ast) || + IS_AST_GEN5(ast) || + IS_AST_GEN4(ast)) { dev->mode_config.max_width = 1920; dev->mode_config.max_height = 2048; } else { diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index a005aec18a02..13e15173f2c5 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -37,41 +37,13 @@ static void ast_post_chip_2300(struct drm_device *dev); static void ast_post_chip_2500(struct drm_device *dev); -void ast_enable_vga(struct drm_device *dev) -{ - struct ast_device *ast = to_ast_device(dev); - - ast_io_write8(ast, AST_IO_VGA_ENABLE_PORT, 0x01); - ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, 0x01); -} - -void ast_enable_mmio(struct drm_device *dev) -{ - struct ast_device *ast = to_ast_device(dev); - - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x06); -} - - -bool ast_is_vga_enabled(struct drm_device *dev) -{ - struct ast_device *ast = to_ast_device(dev); - u8 ch; - - ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT); - - return !!(ch & 0x01); -} - static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff }; -static const u8 extreginfo_ast2300a0[] = { 0x0f, 0x04, 0x1c, 0xff }; static const u8 extreginfo_ast2300[] = { 0x0f, 0x04, 0x1f, 0xff }; static void ast_set_def_ext_reg(struct drm_device *dev) { struct ast_device *ast = to_ast_device(dev); - struct pci_dev *pdev = to_pci_dev(dev->dev); u8 i, index, reg; const u8 *ext_reg_info; @@ -79,13 +51,9 @@ ast_set_def_ext_reg(struct drm_device *dev) for (i = 0x81; i <= 0x9f; i++) ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, 0x00); - if (ast->chip == AST2300 || ast->chip == AST2400 || - ast->chip == AST2500) { - if (pdev->revision >= 0x20) - ext_reg_info = extreginfo_ast2300; - else - ext_reg_info = extreginfo_ast2300a0; - } else + if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast) || IS_AST_GEN6(ast)) + ext_reg_info = extreginfo_ast2300; + else ext_reg_info = extreginfo; index = 0xa0; @@ -104,8 +72,7 @@ ast_set_def_ext_reg(struct drm_device *dev) /* Enable RAMDAC for A1 */ reg = 0x04; - if (ast->chip == AST2300 || ast->chip == AST2400 || - ast->chip == AST2500) + if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast) || IS_AST_GEN6(ast)) reg |= 0x20; ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff, reg); } @@ -281,7 +248,7 @@ static void ast_init_dram_reg(struct drm_device *dev) j = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); if ((j & 0x80) == 0) { /* VGA only */ - if (ast->chip == AST2000) { + if (IS_AST_GEN1(ast)) { dram_reg_info = ast2000_dram_table_data; ast_write32(ast, 0xf004, 0x1e6e0000); ast_write32(ast, 0xf000, 0x1); @@ -290,8 +257,8 @@ static void ast_init_dram_reg(struct drm_device *dev) do { ; } while (ast_read32(ast, 0x10100) != 0xa8); - } else {/* AST2100/1100 */ - if (ast->chip == AST2100 || ast->chip == 2200) + } else { /* GEN2/GEN3 */ + if (ast->chip == AST2100 || ast->chip == AST2200) dram_reg_info = ast2100_dram_table_data; else dram_reg_info = ast1100_dram_table_data; @@ -313,7 +280,7 @@ static void ast_init_dram_reg(struct drm_device *dev) if (dram_reg_info->index == 0xff00) {/* delay fn */ for (i = 0; i < 15; i++) udelay(dram_reg_info->data); - } else if (dram_reg_info->index == 0x4 && ast->chip != AST2000) { + } else if (dram_reg_info->index == 0x4 && !IS_AST_GEN1(ast)) { data = dram_reg_info->data; if (ast->dram_type == AST_DRAM_1Gx16) data = 0x00000d89; @@ -339,15 +306,13 @@ static void ast_init_dram_reg(struct drm_device *dev) cbrdlli_ast2150(ast, 32); /* 32 bits */ } - switch (ast->chip) { - case AST2000: + switch (AST_GEN(ast)) { + case 1: temp = ast_read32(ast, 0x10140); ast_write32(ast, 0x10140, temp | 0x40); break; - case AST1100: - case AST2100: - case AST2200: - case AST2150: + case 2: + case 3: temp = ast_read32(ast, 0x1200c); ast_write32(ast, 0x1200c, temp & 0xfffffffd); temp = ast_read32(ast, 0x12040); @@ -367,25 +332,16 @@ static void ast_init_dram_reg(struct drm_device *dev) void ast_post_gpu(struct drm_device *dev) { struct ast_device *ast = to_ast_device(dev); - struct pci_dev *pdev = to_pci_dev(dev->dev); - u32 reg; - - pci_read_config_dword(pdev, 0x04, ®); - reg |= 0x3; - pci_write_config_dword(pdev, 0x04, reg); - ast_enable_vga(dev); - ast_open_key(ast); - ast_enable_mmio(dev); ast_set_def_ext_reg(dev); - if (ast->chip == AST2600) { + if (IS_AST_GEN7(ast)) { if (ast->tx_chip_types & AST_TX_ASTDP_BIT) ast_dp_launch(dev); } else if (ast->config_mode == ast_use_p2a) { - if (ast->chip == AST2500) + if (IS_AST_GEN6(ast)) ast_post_chip_2500(dev); - else if (ast->chip == AST2300 || ast->chip == AST2400) + else if (IS_AST_GEN5(ast) || IS_AST_GEN4(ast)) ast_post_chip_2300(dev); else ast_init_dram_reg(dev); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 29603561d501..fa0f9a93d50d 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -773,15 +773,13 @@ err_put: return ret; } -static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) +static void atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) { struct drm_device *ddev = platform_get_drvdata(pdev); drm_dev_unregister(ddev); atmel_hlcdc_dc_unload(ddev); drm_dev_put(ddev); - - return 0; } static int atmel_hlcdc_dc_drm_suspend(struct device *dev) @@ -826,7 +824,7 @@ static const struct of_device_id atmel_hlcdc_dc_of_match[] = { static struct platform_driver atmel_hlcdc_dc_platform_driver = { .probe = atmel_hlcdc_dc_drm_probe, - .remove = atmel_hlcdc_dc_drm_remove, + .remove_new = atmel_hlcdc_dc_drm_remove, .driver = { .name = "atmel-hlcdc-display-controller", .pm = pm_sleep_ptr(&atmel_hlcdc_dc_drm_pm_ops), diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index f6822dfa3805..ecb935e46b62 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -54,6 +54,26 @@ #include "cdns-mhdp8546-hdcp.h" #include "cdns-mhdp8546-j721e.h" +static void cdns_mhdp_bridge_hpd_enable(struct drm_bridge *bridge) +{ + struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge); + + /* Enable SW event interrupts */ + if (mhdp->bridge_attached) + writel(readl(mhdp->regs + CDNS_APB_INT_MASK) & + ~CDNS_APB_INT_MASK_SW_EVENT_INT, + mhdp->regs + CDNS_APB_INT_MASK); +} + +static void cdns_mhdp_bridge_hpd_disable(struct drm_bridge *bridge) +{ + struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge); + + writel(readl(mhdp->regs + CDNS_APB_INT_MASK) | + CDNS_APB_INT_MASK_SW_EVENT_INT, + mhdp->regs + CDNS_APB_INT_MASK); +} + static int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp) { int ret, empty; @@ -749,9 +769,7 @@ static int cdns_mhdp_fw_activate(const struct firmware *fw, * MHDP_HW_STOPPED happens only due to driver removal when * bridge should already be detached. */ - if (mhdp->bridge_attached) - writel(~(u32)CDNS_APB_INT_MASK_SW_EVENT_INT, - mhdp->regs + CDNS_APB_INT_MASK); + cdns_mhdp_bridge_hpd_enable(&mhdp->bridge); spin_unlock(&mhdp->start_lock); @@ -1740,8 +1758,7 @@ static int cdns_mhdp_attach(struct drm_bridge *bridge, /* Enable SW event interrupts */ if (hw_ready) - writel(~(u32)CDNS_APB_INT_MASK_SW_EVENT_INT, - mhdp->regs + CDNS_APB_INT_MASK); + cdns_mhdp_bridge_hpd_enable(bridge); return 0; aux_unregister: @@ -2146,6 +2163,27 @@ cdns_mhdp_bridge_atomic_reset(struct drm_bridge *bridge) return &cdns_mhdp_state->base; } +static u32 *cdns_mhdp_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts; + + *num_input_fmts = 0; + + input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) + return NULL; + + *num_input_fmts = 1; + input_fmts[0] = MEDIA_BUS_FMT_RGB121212_1X36; + + return input_fmts; +} + static int cdns_mhdp_atomic_check(struct drm_bridge *bridge, struct drm_bridge_state *bridge_state, struct drm_crtc_state *crtc_state, @@ -2165,6 +2203,13 @@ static int cdns_mhdp_atomic_check(struct drm_bridge *bridge, return -EINVAL; } + /* + * There might be flags negotiation supported in future. + * Set the bus flags in atomic_check statically for now. + */ + if (mhdp->info) + bridge_state->input_bus_cfg.flags = *mhdp->info->input_bus_flags; + mutex_unlock(&mhdp->link_mutex); return 0; } @@ -2184,23 +2229,6 @@ static struct edid *cdns_mhdp_bridge_get_edid(struct drm_bridge *bridge, return cdns_mhdp_get_edid(mhdp, connector); } -static void cdns_mhdp_bridge_hpd_enable(struct drm_bridge *bridge) -{ - struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge); - - /* Enable SW event interrupts */ - if (mhdp->bridge_attached) - writel(~(u32)CDNS_APB_INT_MASK_SW_EVENT_INT, - mhdp->regs + CDNS_APB_INT_MASK); -} - -static void cdns_mhdp_bridge_hpd_disable(struct drm_bridge *bridge) -{ - struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge); - - writel(CDNS_APB_INT_MASK_SW_EVENT_INT, mhdp->regs + CDNS_APB_INT_MASK); -} - static const struct drm_bridge_funcs cdns_mhdp_bridge_funcs = { .atomic_enable = cdns_mhdp_atomic_enable, .atomic_disable = cdns_mhdp_atomic_disable, @@ -2210,6 +2238,7 @@ static const struct drm_bridge_funcs cdns_mhdp_bridge_funcs = { .atomic_duplicate_state = cdns_mhdp_bridge_atomic_duplicate_state, .atomic_destroy_state = cdns_mhdp_bridge_atomic_destroy_state, .atomic_reset = cdns_mhdp_bridge_atomic_reset, + .atomic_get_input_bus_fmts = cdns_mhdp_get_input_bus_fmts, .detect = cdns_mhdp_bridge_detect, .get_edid = cdns_mhdp_bridge_get_edid, .hpd_enable = cdns_mhdp_bridge_hpd_enable, @@ -2529,8 +2558,6 @@ static int cdns_mhdp_probe(struct platform_device *pdev) mhdp->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; mhdp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; - if (mhdp->info) - mhdp->bridge.timings = mhdp->info->timings; ret = phy_init(mhdp->phy); if (ret) { @@ -2617,7 +2644,7 @@ static const struct of_device_id mhdp_ids[] = { #ifdef CONFIG_DRM_CDNS_MHDP8546_J721E { .compatible = "ti,j721e-mhdp8546", .data = &(const struct cdns_mhdp_platform_info) { - .timings = &mhdp_ti_j721e_bridge_timings, + .input_bus_flags = &mhdp_ti_j721e_bridge_input_bus_flags, .ops = &mhdp_ti_j721e_ops, }, }, diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h index bedddd510d17..bad2fc0c7306 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h @@ -336,7 +336,7 @@ struct cdns_mhdp_bridge_state { }; struct cdns_mhdp_platform_info { - const struct drm_bridge_timings *timings; + const u32 *input_bus_flags; const struct mhdp_platform_ops *ops; }; diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.c index dfe1b59514f7..12d04be4e242 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.c @@ -71,8 +71,7 @@ const struct mhdp_platform_ops mhdp_ti_j721e_ops = { .disable = cdns_mhdp_j721e_disable, }; -const struct drm_bridge_timings mhdp_ti_j721e_bridge_timings = { - .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE | - DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE | - DRM_BUS_FLAG_DE_HIGH, -}; +const u32 +mhdp_ti_j721e_bridge_input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE | + DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE | + DRM_BUS_FLAG_DE_HIGH; diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.h index 97d20d115a24..5ddca07a4255 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.h +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.h @@ -14,6 +14,6 @@ struct mhdp_platform_ops; extern const struct mhdp_platform_ops mhdp_ti_j721e_ops; -extern const struct drm_bridge_timings mhdp_ti_j721e_bridge_timings; +extern const u32 mhdp_ti_j721e_bridge_input_bus_flags; #endif /* !CDNS_MHDP8546_J721E_H */ diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index 2a57e804ea02..22c84d29c2bc 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -28,6 +28,8 @@ #define EDID_BLOCK_SIZE 128 #define EDID_NUM_BLOCKS 2 +#define FW_FILE "lt9611uxc_fw.bin" + struct lt9611uxc { struct device *dev; struct drm_bridge bridge; @@ -754,7 +756,7 @@ static int lt9611uxc_firmware_update(struct lt9611uxc *lt9611uxc) REG_SEQ0(0x805a, 0x00), }; - ret = request_firmware(&fw, "lt9611uxc_fw.bin", lt9611uxc->dev); + ret = request_firmware(&fw, FW_FILE, lt9611uxc->dev); if (ret < 0) return ret; @@ -1019,3 +1021,5 @@ module_i2c_driver(lt9611uxc_driver); MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>"); MODULE_LICENSE("GPL v2"); + +MODULE_FIRMWARE(FW_FILE); diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 8801cdd033b5..8161b1a1a4b1 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -105,7 +105,6 @@ struct ps8640 { struct gpio_desc *gpio_reset; struct gpio_desc *gpio_powerdown; struct device_link *link; - struct edid *edid; bool pre_enabled; bool need_post_hpd_delay; }; @@ -155,23 +154,6 @@ static inline struct ps8640 *aux_to_ps8640(struct drm_dp_aux *aux) return container_of(aux, struct ps8640, aux); } -static bool ps8640_of_panel_on_aux_bus(struct device *dev) -{ - struct device_node *bus, *panel; - - bus = of_get_child_by_name(dev->of_node, "aux-bus"); - if (!bus) - return false; - - panel = of_get_child_by_name(bus, "panel"); - of_node_put(bus); - if (!panel) - return false; - of_node_put(panel); - - return true; -} - static int _ps8640_wait_hpd_asserted(struct ps8640 *ps_bridge, unsigned long wait_us) { struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL]; @@ -539,50 +521,6 @@ static void ps8640_bridge_detach(struct drm_bridge *bridge) device_link_del(ps_bridge->link); } -static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge, - struct drm_connector *connector) -{ - struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); - struct device *dev = &ps_bridge->page[PAGE0_DP_CNTL]->dev; - bool poweroff = !ps_bridge->pre_enabled; - - if (!ps_bridge->edid) { - /* - * When we end calling get_edid() triggered by an ioctl, i.e - * - * drm_mode_getconnector (ioctl) - * -> drm_helper_probe_single_connector_modes - * -> drm_bridge_connector_get_modes - * -> ps8640_bridge_get_edid - * - * We need to make sure that what we need is enabled before - * reading EDID, for this chip, we need to do a full poweron, - * otherwise it will fail. - */ - if (poweroff) - drm_atomic_bridge_chain_pre_enable(bridge, - connector->state->state); - - ps_bridge->edid = drm_get_edid(connector, - ps_bridge->page[PAGE0_DP_CNTL]->adapter); - - /* - * If we call the get_edid() function without having enabled the - * chip before, return the chip to its original power state. - */ - if (poweroff) - drm_atomic_bridge_chain_post_disable(bridge, - connector->state->state); - } - - if (!ps_bridge->edid) { - dev_err(dev, "Failed to get EDID\n"); - return NULL; - } - - return drm_edid_duplicate(ps_bridge->edid); -} - static void ps8640_runtime_disable(void *data) { pm_runtime_dont_use_autosuspend(data); @@ -592,7 +530,6 @@ static void ps8640_runtime_disable(void *data) static const struct drm_bridge_funcs ps8640_bridge_funcs = { .attach = ps8640_bridge_attach, .detach = ps8640_bridge_detach, - .get_edid = ps8640_bridge_get_edid, .atomic_post_disable = ps8640_atomic_post_disable, .atomic_pre_enable = ps8640_atomic_pre_enable, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, @@ -706,14 +643,6 @@ static int ps8640_probe(struct i2c_client *client) ps_bridge->bridge.type = DRM_MODE_CONNECTOR_eDP; /* - * In the device tree, if panel is listed under aux-bus of the bridge - * node, panel driver should be able to retrieve EDID by itself using - * aux-bus. So let's not set DRM_BRIDGE_OP_EDID here. - */ - if (!ps8640_of_panel_on_aux_bus(&client->dev)) - ps_bridge->bridge.ops = DRM_BRIDGE_OP_EDID; - - /* * Get MIPI DSI resources early. These can return -EPROBE_DEFER so * we want to get them out of the way sooner. */ @@ -777,13 +706,6 @@ static int ps8640_probe(struct i2c_client *client) return ret; } -static void ps8640_remove(struct i2c_client *client) -{ - struct ps8640 *ps_bridge = i2c_get_clientdata(client); - - kfree(ps_bridge->edid); -} - static const struct of_device_id ps8640_match[] = { { .compatible = "parade,ps8640" }, { } @@ -792,7 +714,6 @@ MODULE_DEVICE_TABLE(of, ps8640_match); static struct i2c_driver ps8640_driver = { .probe = ps8640_probe, - .remove = ps8640_remove, .driver = { .name = "ps8640", .of_match_table = ps8640_match, diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index 043b8109e64a..9b7a00bafeaa 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -1009,7 +1009,7 @@ static int samsung_dsim_wait_for_hdr_fifo(struct samsung_dsim *dsi) do { u32 reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG); - if (!(reg & DSIM_SFR_HEADER_FULL)) + if (reg & DSIM_SFR_HEADER_EMPTY) return 0; if (!cond_resched()) diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index aac239729a1d..2bdc5b439beb 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -473,6 +473,41 @@ static struct edid *sii902x_bridge_get_edid(struct drm_bridge *bridge, return sii902x_get_edid(sii902x, connector); } +static u32 *sii902x_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts; + + *num_input_fmts = 0; + + input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) + return NULL; + + input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + *num_input_fmts = 1; + + return input_fmts; +} + +static int sii902x_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + /* + * There might be flags negotiation supported in future but + * set the bus flags in atomic_check statically for now. + */ + bridge_state->input_bus_cfg.flags = bridge->timings->input_bus_flags; + + return 0; +} + static const struct drm_bridge_funcs sii902x_bridge_funcs = { .attach = sii902x_bridge_attach, .mode_set = sii902x_bridge_mode_set, @@ -480,6 +515,11 @@ static const struct drm_bridge_funcs sii902x_bridge_funcs = { .enable = sii902x_bridge_enable, .detect = sii902x_bridge_detect, .get_edid = sii902x_bridge_get_edid, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_get_input_bus_fmts = sii902x_bridge_atomic_get_input_bus_fmts, + .atomic_check = sii902x_bridge_atomic_check, }; static int sii902x_mute(struct sii902x *sii902x, bool mute) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 7b66f362afd8..9a3db5234a0e 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -49,20 +49,6 @@ #define HDMI14_MAX_TMDSCLK 340000000 -enum hdmi_datamap { - RGB444_8B = 0x01, - RGB444_10B = 0x03, - RGB444_12B = 0x05, - RGB444_16B = 0x07, - YCbCr444_8B = 0x09, - YCbCr444_10B = 0x0B, - YCbCr444_12B = 0x0D, - YCbCr444_16B = 0x0F, - YCbCr422_8B = 0x16, - YCbCr422_10B = 0x14, - YCbCr422_12B = 0x12, -}; - static const u16 csc_coeff_default[3][4] = { { 0x2000, 0x0000, 0x0000, 0x0000 }, { 0x0000, 0x2000, 0x0000, 0x0000 }, @@ -856,10 +842,10 @@ static void dw_hdmi_gp_audio_enable(struct dw_hdmi *hdmi) if (pdata->enable_audio) pdata->enable_audio(hdmi, - hdmi->channels, - hdmi->sample_width, - hdmi->sample_rate, - hdmi->sample_non_pcm); + hdmi->channels, + hdmi->sample_width, + hdmi->sample_rate, + hdmi->sample_non_pcm); } static void dw_hdmi_gp_audio_disable(struct dw_hdmi *hdmi) @@ -2710,9 +2696,10 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, /* Default 8bit fallback */ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24; - *num_output_fmts = i; - - return output_fmts; + if (drm_mode_is_420_only(info, mode)) { + *num_output_fmts = i; + return output_fmts; + } } /* @@ -3346,6 +3333,12 @@ static int dw_hdmi_parse_dt(struct dw_hdmi *hdmi) return 0; } +bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi) +{ + return hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format); +} +EXPORT_SYMBOL_GPL(dw_hdmi_bus_fmt_is_420); + struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, const struct dw_hdmi_plat_data *plat_data) { diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index b2efecf7d160..4291798bd70f 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -265,6 +265,7 @@ struct dw_mipi_dsi { struct dw_mipi_dsi *master; /* dual-dsi master ptr */ struct dw_mipi_dsi *slave; /* dual-dsi slave ptr */ + struct drm_display_mode mode; const struct dw_mipi_dsi_plat_data *plat_data; }; @@ -332,6 +333,7 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, if (IS_ERR(bridge)) return PTR_ERR(bridge); + bridge->pre_enable_prev_first = true; dsi->panel_bridge = bridge; drm_bridge_add(&dsi->bridge); @@ -859,15 +861,6 @@ static void dw_mipi_dsi_bridge_post_atomic_disable(struct drm_bridge *bridge, */ dw_mipi_dsi_set_mode(dsi, 0); - /* - * TODO Only way found to call panel-bridge post_disable & - * panel unprepare before the dsi "final" disable... - * This needs to be fixed in the drm_bridge framework and the API - * needs to be updated to manage our own call chains... - */ - if (dsi->panel_bridge->funcs->post_disable) - dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge); - if (phy_ops->power_off) phy_ops->power_off(dsi->plat_data->priv_data); @@ -942,15 +935,25 @@ static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi, phy_ops->power_on(dsi->plat_data->priv_data); } +static void dw_mipi_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + + /* Power up the dsi ctl into a command mode */ + dw_mipi_dsi_mode_set(dsi, &dsi->mode); + if (dsi->slave) + dw_mipi_dsi_mode_set(dsi->slave, &dsi->mode); +} + static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); - dw_mipi_dsi_mode_set(dsi, adjusted_mode); - if (dsi->slave) - dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode); + /* Store the display mode for later use in pre_enable callback */ + drm_mode_copy(&dsi->mode, adjusted_mode); } static void dw_mipi_dsi_bridge_atomic_enable(struct drm_bridge *bridge, @@ -1004,6 +1007,7 @@ static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_pre_enable = dw_mipi_dsi_bridge_atomic_pre_enable, .atomic_enable = dw_mipi_dsi_bridge_atomic_enable, .atomic_post_disable = dw_mipi_dsi_bridge_post_atomic_disable, .mode_set = dw_mipi_dsi_bridge_mode_set, diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c index 5641395fd310..46198af9eebb 100644 --- a/drivers/gpu/drm/bridge/tc358762.c +++ b/drivers/gpu/drm/bridge/tc358762.c @@ -41,8 +41,17 @@ #define DSI_LANEENABLE 0x0210 /* Enables each lane */ #define DSI_RX_START 1 -/* LCDC/DPI Host Registers */ -#define LCDCTRL 0x0420 +/* LCDC/DPI Host Registers, based on guesswork that this matches TC358764 */ +#define LCDCTRL 0x0420 /* Video Path Control */ +#define LCDCTRL_MSF BIT(0) /* Magic square in RGB666 */ +#define LCDCTRL_VTGEN BIT(4)/* Use chip clock for timing */ +#define LCDCTRL_UNK6 BIT(6) /* Unknown */ +#define LCDCTRL_EVTMODE BIT(5) /* Event mode */ +#define LCDCTRL_RGB888 BIT(8) /* RGB888 mode */ +#define LCDCTRL_HSPOL BIT(17) /* Polarity of HSYNC signal */ +#define LCDCTRL_DEPOL BIT(18) /* Polarity of DE signal */ +#define LCDCTRL_VSPOL BIT(19) /* Polarity of VSYNC signal */ +#define LCDCTRL_VSDELAY(v) (((v) & 0xfff) << 20) /* VSYNC delay */ /* SPI Master Registers */ #define SPICMR 0x0450 @@ -65,6 +74,7 @@ struct tc358762 { struct regulator *regulator; struct drm_bridge *panel_bridge; struct gpio_desc *reset_gpio; + struct drm_display_mode mode; bool pre_enabled; int error; }; @@ -105,6 +115,8 @@ static inline struct tc358762 *bridge_to_tc358762(struct drm_bridge *bridge) static int tc358762_init(struct tc358762 *ctx) { + u32 lcdctrl; + tc358762_write(ctx, DSI_LANEENABLE, LANEENABLE_L0EN | LANEENABLE_CLEN); tc358762_write(ctx, PPI_D0S_CLRSIPOCOUNT, 5); @@ -114,7 +126,18 @@ static int tc358762_init(struct tc358762 *ctx) tc358762_write(ctx, PPI_LPTXTIMECNT, LPX_PERIOD); tc358762_write(ctx, SPICMR, 0x00); - tc358762_write(ctx, LCDCTRL, 0x00100150); + + lcdctrl = LCDCTRL_VSDELAY(1) | LCDCTRL_RGB888 | + LCDCTRL_UNK6 | LCDCTRL_VTGEN; + + if (ctx->mode.flags & DRM_MODE_FLAG_NHSYNC) + lcdctrl |= LCDCTRL_HSPOL; + + if (ctx->mode.flags & DRM_MODE_FLAG_NVSYNC) + lcdctrl |= LCDCTRL_VSPOL; + + tc358762_write(ctx, LCDCTRL, lcdctrl); + tc358762_write(ctx, SYSCTRL, 0x040f); msleep(100); @@ -126,7 +149,7 @@ static int tc358762_init(struct tc358762 *ctx) return tc358762_clear_error(ctx); } -static void tc358762_post_disable(struct drm_bridge *bridge) +static void tc358762_post_disable(struct drm_bridge *bridge, struct drm_bridge_state *state) { struct tc358762 *ctx = bridge_to_tc358762(bridge); int ret; @@ -148,7 +171,7 @@ static void tc358762_post_disable(struct drm_bridge *bridge) dev_err(ctx->dev, "error disabling regulators (%d)\n", ret); } -static void tc358762_pre_enable(struct drm_bridge *bridge) +static void tc358762_pre_enable(struct drm_bridge *bridge, struct drm_bridge_state *state) { struct tc358762 *ctx = bridge_to_tc358762(bridge); int ret; @@ -162,11 +185,17 @@ static void tc358762_pre_enable(struct drm_bridge *bridge) usleep_range(5000, 10000); } + ctx->pre_enabled = true; +} + +static void tc358762_enable(struct drm_bridge *bridge, struct drm_bridge_state *state) +{ + struct tc358762 *ctx = bridge_to_tc358762(bridge); + int ret; + ret = tc358762_init(ctx); if (ret < 0) dev_err(ctx->dev, "error initializing bridge (%d)\n", ret); - - ctx->pre_enabled = true; } static int tc358762_attach(struct drm_bridge *bridge, @@ -178,10 +207,24 @@ static int tc358762_attach(struct drm_bridge *bridge, bridge, flags); } +static void tc358762_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adj) +{ + struct tc358762 *ctx = bridge_to_tc358762(bridge); + + drm_mode_copy(&ctx->mode, mode); +} + static const struct drm_bridge_funcs tc358762_bridge_funcs = { - .post_disable = tc358762_post_disable, - .pre_enable = tc358762_pre_enable, + .atomic_post_disable = tc358762_post_disable, + .atomic_pre_enable = tc358762_pre_enable, + .atomic_enable = tc358762_enable, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, .attach = tc358762_attach, + .mode_set = tc358762_bridge_mode_set, }; static int tc358762_parse_dt(struct tc358762 *ctx) @@ -231,7 +274,7 @@ static int tc358762_probe(struct mipi_dsi_device *dsi) dsi->lanes = 1; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | - MIPI_DSI_MODE_LPM; + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO_HSE; ret = tc358762_parse_dt(ctx); if (ret < 0) diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index f85654f1b104..deccb3995022 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -42,10 +42,10 @@ /* Video path registers */ #define VP_CTRL 0x0450 /* Video Path Control */ -#define VP_CTRL_MSF(v) FLD_VAL(v, 0, 0) /* Magic square in RGB666 */ -#define VP_CTRL_VTGEN(v) FLD_VAL(v, 4, 4) /* Use chip clock for timing */ -#define VP_CTRL_EVTMODE(v) FLD_VAL(v, 5, 5) /* Event mode */ -#define VP_CTRL_RGB888(v) FLD_VAL(v, 8, 8) /* RGB888 mode */ +#define VP_CTRL_MSF BIT(0) /* Magic square in RGB666 */ +#define VP_CTRL_VTGEN BIT(4) /* Use chip clock for timing */ +#define VP_CTRL_EVTMODE BIT(5) /* Event mode */ +#define VP_CTRL_RGB888 BIT(8) /* RGB888 mode */ #define VP_CTRL_VSDELAY(v) FLD_VAL(v, 31, 20) /* VSYNC delay */ #define VP_CTRL_HSPOL BIT(17) /* Polarity of HSYNC signal */ #define VP_CTRL_DEPOL BIT(18) /* Polarity of DE signal */ @@ -176,7 +176,7 @@ static void tc358764_read(struct tc358764 *ctx, u16 addr, u32 *val) if (ret >= 0) le32_to_cpus(val); - dev_dbg(ctx->dev, "read: %d, addr: %d\n", addr, *val); + dev_dbg(ctx->dev, "read: addr=0x%04x data=0x%08x\n", addr, *val); } static void tc358764_write(struct tc358764 *ctx, u16 addr, u32 val) @@ -233,8 +233,8 @@ static int tc358764_init(struct tc358764 *ctx) tc358764_write(ctx, DSI_STARTDSI, DSI_RX_START); /* configure video path */ - tc358764_write(ctx, VP_CTRL, VP_CTRL_VSDELAY(15) | VP_CTRL_RGB888(1) | - VP_CTRL_EVTMODE(1) | VP_CTRL_HSPOL | VP_CTRL_VSPOL); + tc358764_write(ctx, VP_CTRL, VP_CTRL_VSDELAY(15) | VP_CTRL_RGB888 | + VP_CTRL_EVTMODE | VP_CTRL_HSPOL | VP_CTRL_VSPOL); /* reset PHY */ tc358764_write(ctx, LV_PHY0, LV_PHY0_RST(1) | diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 65dc842e31f0..eaa7edb080fa 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -2215,13 +2215,6 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc) return -EINVAL; } -static void tc_clk_disable(void *data) -{ - struct clk *refclk = data; - - clk_disable_unprepare(refclk); -} - static int tc_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -2238,20 +2231,10 @@ static int tc_probe(struct i2c_client *client) if (ret) return ret; - tc->refclk = devm_clk_get(dev, "ref"); - if (IS_ERR(tc->refclk)) { - ret = PTR_ERR(tc->refclk); - dev_err(dev, "Failed to get refclk: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(tc->refclk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, tc_clk_disable, tc->refclk); - if (ret) - return ret; + tc->refclk = devm_clk_get_enabled(dev, "ref"); + if (IS_ERR(tc->refclk)) + return dev_err_probe(dev, PTR_ERR(tc->refclk), + "Failed to get and enable the ref clk\n"); /* tRSTW = 100 cycles , at 13 MHz that is ~7.69 us */ usleep_range(10, 15); diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index c06390da9ffd..22b07260a78e 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -206,12 +206,55 @@ static enum drm_mode_status tfp410_mode_valid(struct drm_bridge *bridge, return MODE_OK; } +static u32 *tfp410_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); + u32 *input_fmts; + + *num_input_fmts = 0; + + input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) + return NULL; + + *num_input_fmts = 1; + input_fmts[0] = dvi->bus_format; + + return input_fmts; +} + +static int tfp410_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); + + /* + * There might be flags negotiation supported in future. + * Set the bus flags in atomic_check statically for now. + */ + bridge_state->input_bus_cfg.flags = dvi->timings.input_bus_flags; + + return 0; +} + static const struct drm_bridge_funcs tfp410_bridge_funcs = { .attach = tfp410_attach, .detach = tfp410_detach, .enable = tfp410_enable, .disable = tfp410_disable, .mode_valid = tfp410_mode_valid, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_get_input_bus_fmts = tfp410_get_input_bus_fmts, + .atomic_check = tfp410_atomic_check, }; static const struct drm_bridge_timings tfp410_default_timings = { diff --git a/drivers/gpu/drm/display/drm_hdcp_helper.c b/drivers/gpu/drm/display/drm_hdcp_helper.c index e78999c72bd7..a3f0e6d96105 100644 --- a/drivers/gpu/drm/display/drm_hdcp_helper.c +++ b/drivers/gpu/drm/display/drm_hdcp_helper.c @@ -415,7 +415,7 @@ void drm_hdcp_update_content_protection(struct drm_connector *connector, return; state->content_protection = val; - drm_sysfs_connector_status_event(connector, - dev->mode_config.content_protection_property); + drm_sysfs_connector_property_event(connector, + dev->mode_config.content_protection_property); } EXPORT_SYMBOL(drm_hdcp_update_content_protection); diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index d867e7f9f2cd..98d3b10c08ae 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -374,16 +374,25 @@ drm_atomic_replace_property_blob_from_id(struct drm_device *dev, if (blob_id != 0) { new_blob = drm_property_lookup_blob(dev, blob_id); - if (new_blob == NULL) + if (new_blob == NULL) { + drm_dbg_atomic(dev, + "cannot find blob ID %llu\n", blob_id); return -EINVAL; + } if (expected_size > 0 && new_blob->length != expected_size) { + drm_dbg_atomic(dev, + "[BLOB:%d] length %zu different from expected %zu\n", + new_blob->base.id, new_blob->length, expected_size); drm_property_blob_put(new_blob); return -EINVAL; } if (expected_elem_size > 0 && new_blob->length % expected_elem_size != 0) { + drm_dbg_atomic(dev, + "[BLOB:%d] length %zu not divisible by element size %zu\n", + new_blob->base.id, new_blob->length, expected_elem_size); drm_property_blob_put(new_blob); return -EINVAL; } @@ -454,7 +463,7 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc, return crtc->funcs->atomic_set_property(crtc, state, property, val); } else { drm_dbg_atomic(crtc->dev, - "[CRTC:%d:%s] unknown property [PROP:%d:%s]]\n", + "[CRTC:%d:%s] unknown property [PROP:%d:%s]\n", crtc->base.id, crtc->name, property->base.id, property->name); return -EINVAL; @@ -489,8 +498,13 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc, *val = state->scaling_filter; else if (crtc->funcs->atomic_get_property) return crtc->funcs->atomic_get_property(crtc, state, property, val); - else + else { + drm_dbg_atomic(dev, + "[CRTC:%d:%s] unknown property [PROP:%d:%s]\n", + crtc->base.id, crtc->name, + property->base.id, property->name); return -EINVAL; + } return 0; } @@ -525,8 +539,12 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, } else if (property == config->prop_crtc_id) { struct drm_crtc *crtc = drm_crtc_find(dev, file_priv, val); - if (val && !crtc) + if (val && !crtc) { + drm_dbg_atomic(dev, + "[PROP:%d:%s] cannot find CRTC with ID %llu\n", + property->base.id, property->name, val); return -EACCES; + } return drm_atomic_set_crtc_for_plane(state, crtc); } else if (property == config->prop_crtc_x) { state->crtc_x = U642I64(val); @@ -577,7 +595,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, property, val); } else { drm_dbg_atomic(plane->dev, - "[PLANE:%d:%s] unknown property [PROP:%d:%s]]\n", + "[PLANE:%d:%s] unknown property [PROP:%d:%s]\n", plane->base.id, plane->name, property->base.id, property->name); return -EINVAL; @@ -636,6 +654,10 @@ drm_atomic_plane_get_property(struct drm_plane *plane, } else if (plane->funcs->atomic_get_property) { return plane->funcs->atomic_get_property(plane, state, property, val); } else { + drm_dbg_atomic(dev, + "[PLANE:%d:%s] unknown property [PROP:%d:%s]\n", + plane->base.id, plane->name, + property->base.id, property->name); return -EINVAL; } @@ -677,14 +699,21 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, if (property == config->prop_crtc_id) { struct drm_crtc *crtc = drm_crtc_find(dev, file_priv, val); - if (val && !crtc) + if (val && !crtc) { + drm_dbg_atomic(dev, + "[PROP:%d:%s] cannot find CRTC with ID %llu\n", + property->base.id, property->name, val); return -EACCES; + } return drm_atomic_set_crtc_for_connector(state, crtc); } else if (property == config->dpms_property) { /* setting DPMS property requires special handling, which * is done in legacy setprop path for us. Disallow (for * now?) atomic writes to DPMS property: */ + drm_dbg_atomic(dev, + "legacy [PROP:%d:%s] can only be set via legacy uAPI\n", + property->base.id, property->name); return -EINVAL; } else if (property == config->tv_select_subconnector_property) { state->tv.select_subconnector = val; @@ -774,7 +803,7 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, state, property, val); } else { drm_dbg_atomic(connector->dev, - "[CONNECTOR:%d:%s] unknown property [PROP:%d:%s]]\n", + "[CONNECTOR:%d:%s] unknown property [PROP:%d:%s]\n", connector->base.id, connector->name, property->base.id, property->name); return -EINVAL; @@ -856,6 +885,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector, return connector->funcs->atomic_get_property(connector, state, property, val); } else { + drm_dbg_atomic(dev, + "[CONNECTOR:%d:%s] unknown property [PROP:%d:%s]\n", + connector->base.id, connector->name, + property->base.id, property->name); return -EINVAL; } @@ -894,6 +927,7 @@ int drm_atomic_get_property(struct drm_mode_object *obj, break; } default: + drm_dbg_atomic(dev, "[OBJECT:%d] has no properties\n", obj->id); ret = -EINVAL; break; } @@ -1030,6 +1064,7 @@ int drm_atomic_set_property(struct drm_atomic_state *state, break; } default: + drm_dbg_atomic(prop->dev, "[OBJECT:%d] has no properties\n", obj->id); ret = -EINVAL; break; } @@ -1230,8 +1265,10 @@ static int prepare_signaling(struct drm_device *dev, * Having this flag means user mode pends on event which will never * reach due to lack of at least one CRTC for signaling */ - if (c == 0 && (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) + if (c == 0 && (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) { + drm_dbg_atomic(dev, "need at least one CRTC for DRM_MODE_PAGE_FLIP_EVENT"); return -EINVAL; + } return 0; } @@ -1364,11 +1401,13 @@ retry: obj = drm_mode_object_find(dev, file_priv, obj_id, DRM_MODE_OBJECT_ANY); if (!obj) { + drm_dbg_atomic(dev, "cannot find object ID %d", obj_id); ret = -ENOENT; goto out; } if (!obj->properties) { + drm_dbg_atomic(dev, "[OBJECT:%d] has no properties", obj_id); drm_mode_object_put(obj); ret = -ENOENT; goto out; @@ -1395,6 +1434,9 @@ retry: prop = drm_mode_obj_find_prop_id(obj, prop_id); if (!prop) { + drm_dbg_atomic(dev, + "[OBJECT:%d] cannot find property ID %d", + obj_id, prop_id); drm_mode_object_put(obj); ret = -ENOENT; goto out; diff --git a/drivers/gpu/drm/drm_bridge_connector.c b/drivers/gpu/drm/drm_bridge_connector.c index 19ae4a177ac3..fc6f16e14f36 100644 --- a/drivers/gpu/drm/drm_bridge_connector.c +++ b/drivers/gpu/drm/drm_bridge_connector.c @@ -125,7 +125,7 @@ static void drm_bridge_connector_hpd_cb(void *cb_data, drm_bridge_connector_hpd_notify(connector, status); - drm_kms_helper_hotplug_event(dev); + drm_kms_helper_connector_hotplug_event(connector); } static void drm_bridge_connector_enable_hpd(struct drm_connector *connector) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 3ed4cfcb350c..bf8371dc2a61 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -2730,10 +2730,10 @@ static int drm_connector_privacy_screen_notifier( drm_connector_update_privacy_screen_properties(connector, true); drm_modeset_unlock(&dev->mode_config.connection_mutex); - drm_sysfs_connector_status_event(connector, - connector->privacy_screen_sw_state_property); - drm_sysfs_connector_status_event(connector, - connector->privacy_screen_hw_state_property); + drm_sysfs_connector_property_event(connector, + connector->privacy_screen_sw_state_property); + drm_sysfs_connector_property_event(connector, + connector->privacy_screen_hw_state_property); return NOTIFY_DONE; } diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index e0dbd9140726..f95152fac427 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -230,6 +230,7 @@ static const struct edid_quirk { /* OSVR HDK and HDK2 VR Headsets */ EDID_QUIRK('S', 'V', 'R', 0x1019, EDID_QUIRK_NON_DESKTOP), + EDID_QUIRK('A', 'U', 'O', 0x1111, EDID_QUIRK_NON_DESKTOP), }; /* @@ -3962,7 +3963,7 @@ static int drm_cvt_modes(struct drm_connector *connector, struct drm_display_mode *newmode; struct drm_device *dev = connector->dev; const struct cvt_timing *cvt; - const int rates[] = { 60, 85, 75, 60, 50 }; + static const int rates[] = { 60, 85, 75, 60, 50 }; const u8 empty[3] = { 0, 0, 0 }; for (i = 0; i < 4; i++) { diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c new file mode 100644 index 000000000000..ff69cf0fb42a --- /dev/null +++ b/drivers/gpu/drm/drm_exec.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +#include <drm/drm_exec.h> +#include <drm/drm_gem.h> +#include <linux/dma-resv.h> + +/** + * DOC: Overview + * + * This component mainly abstracts the retry loop necessary for locking + * multiple GEM objects while preparing hardware operations (e.g. command + * submissions, page table updates etc..). + * + * If a contention is detected while locking a GEM object the cleanup procedure + * unlocks all previously locked GEM objects and locks the contended one first + * before locking any further objects. + * + * After an object is locked fences slots can optionally be reserved on the + * dma_resv object inside the GEM object. + * + * A typical usage pattern should look like this:: + * + * struct drm_gem_object *obj; + * struct drm_exec exec; + * unsigned long index; + * int ret; + * + * drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); + * drm_exec_until_all_locked(&exec) { + * ret = drm_exec_prepare_obj(&exec, boA, 1); + * drm_exec_retry_on_contention(&exec); + * if (ret) + * goto error; + * + * ret = drm_exec_prepare_obj(&exec, boB, 1); + * drm_exec_retry_on_contention(&exec); + * if (ret) + * goto error; + * } + * + * drm_exec_for_each_locked_object(&exec, index, obj) { + * dma_resv_add_fence(obj->resv, fence, DMA_RESV_USAGE_READ); + * ... + * } + * drm_exec_fini(&exec); + * + * See struct dma_exec for more details. + */ + +/* Dummy value used to initially enter the retry loop */ +#define DRM_EXEC_DUMMY ((void *)~0) + +/* Unlock all objects and drop references */ +static void drm_exec_unlock_all(struct drm_exec *exec) +{ + struct drm_gem_object *obj; + unsigned long index; + + drm_exec_for_each_locked_object(exec, index, obj) { + dma_resv_unlock(obj->resv); + drm_gem_object_put(obj); + } + + drm_gem_object_put(exec->prelocked); + exec->prelocked = NULL; +} + +/** + * drm_exec_init - initialize a drm_exec object + * @exec: the drm_exec object to initialize + * @flags: controls locking behavior, see DRM_EXEC_* defines + * + * Initialize the object and make sure that we can track locked objects. + */ +void drm_exec_init(struct drm_exec *exec, uint32_t flags) +{ + exec->flags = flags; + exec->objects = kmalloc(PAGE_SIZE, GFP_KERNEL); + + /* If allocation here fails, just delay that till the first use */ + exec->max_objects = exec->objects ? PAGE_SIZE / sizeof(void *) : 0; + exec->num_objects = 0; + exec->contended = DRM_EXEC_DUMMY; + exec->prelocked = NULL; +} +EXPORT_SYMBOL(drm_exec_init); + +/** + * drm_exec_fini - finalize a drm_exec object + * @exec: the drm_exec object to finalize + * + * Unlock all locked objects, drop the references to objects and free all memory + * used for tracking the state. + */ +void drm_exec_fini(struct drm_exec *exec) +{ + drm_exec_unlock_all(exec); + kvfree(exec->objects); + if (exec->contended != DRM_EXEC_DUMMY) { + drm_gem_object_put(exec->contended); + ww_acquire_fini(&exec->ticket); + } +} +EXPORT_SYMBOL(drm_exec_fini); + +/** + * drm_exec_cleanup - cleanup when contention is detected + * @exec: the drm_exec object to cleanup + * + * Cleanup the current state and return true if we should stay inside the retry + * loop, false if there wasn't any contention detected and we can keep the + * objects locked. + */ +bool drm_exec_cleanup(struct drm_exec *exec) +{ + if (likely(!exec->contended)) { + ww_acquire_done(&exec->ticket); + return false; + } + + if (likely(exec->contended == DRM_EXEC_DUMMY)) { + exec->contended = NULL; + ww_acquire_init(&exec->ticket, &reservation_ww_class); + return true; + } + + drm_exec_unlock_all(exec); + exec->num_objects = 0; + return true; +} +EXPORT_SYMBOL(drm_exec_cleanup); + +/* Track the locked object in the array */ +static int drm_exec_obj_locked(struct drm_exec *exec, + struct drm_gem_object *obj) +{ + if (unlikely(exec->num_objects == exec->max_objects)) { + size_t size = exec->max_objects * sizeof(void *); + void *tmp; + + tmp = kvrealloc(exec->objects, size, size + PAGE_SIZE, + GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + exec->objects = tmp; + exec->max_objects += PAGE_SIZE / sizeof(void *); + } + drm_gem_object_get(obj); + exec->objects[exec->num_objects++] = obj; + + return 0; +} + +/* Make sure the contended object is locked first */ +static int drm_exec_lock_contended(struct drm_exec *exec) +{ + struct drm_gem_object *obj = exec->contended; + int ret; + + if (likely(!obj)) + return 0; + + /* Always cleanup the contention so that error handling can kick in */ + exec->contended = NULL; + if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT) { + ret = dma_resv_lock_slow_interruptible(obj->resv, + &exec->ticket); + if (unlikely(ret)) + goto error_dropref; + } else { + dma_resv_lock_slow(obj->resv, &exec->ticket); + } + + ret = drm_exec_obj_locked(exec, obj); + if (unlikely(ret)) + goto error_unlock; + + exec->prelocked = obj; + return 0; + +error_unlock: + dma_resv_unlock(obj->resv); + +error_dropref: + drm_gem_object_put(obj); + return ret; +} + +/** + * drm_exec_lock_obj - lock a GEM object for use + * @exec: the drm_exec object with the state + * @obj: the GEM object to lock + * + * Lock a GEM object for use and grab a reference to it. + * + * Returns: -EDEADLK if a contention is detected, -EALREADY when object is + * already locked (can be suppressed by setting the DRM_EXEC_IGNORE_DUPLICATES + * flag), -ENOMEM when memory allocation failed and zero for success. + */ +int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj) +{ + int ret; + + ret = drm_exec_lock_contended(exec); + if (unlikely(ret)) + return ret; + + if (exec->prelocked == obj) { + drm_gem_object_put(exec->prelocked); + exec->prelocked = NULL; + return 0; + } + + if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT) + ret = dma_resv_lock_interruptible(obj->resv, &exec->ticket); + else + ret = dma_resv_lock(obj->resv, &exec->ticket); + + if (unlikely(ret == -EDEADLK)) { + drm_gem_object_get(obj); + exec->contended = obj; + return -EDEADLK; + } + + if (unlikely(ret == -EALREADY) && + exec->flags & DRM_EXEC_IGNORE_DUPLICATES) + return 0; + + if (unlikely(ret)) + return ret; + + ret = drm_exec_obj_locked(exec, obj); + if (ret) + goto error_unlock; + + return 0; + +error_unlock: + dma_resv_unlock(obj->resv); + return ret; +} +EXPORT_SYMBOL(drm_exec_lock_obj); + +/** + * drm_exec_unlock_obj - unlock a GEM object in this exec context + * @exec: the drm_exec object with the state + * @obj: the GEM object to unlock + * + * Unlock the GEM object and remove it from the collection of locked objects. + * Should only be used to unlock the most recently locked objects. It's not time + * efficient to unlock objects locked long ago. + */ +void drm_exec_unlock_obj(struct drm_exec *exec, struct drm_gem_object *obj) +{ + unsigned int i; + + for (i = exec->num_objects; i--;) { + if (exec->objects[i] == obj) { + dma_resv_unlock(obj->resv); + for (++i; i < exec->num_objects; ++i) + exec->objects[i - 1] = exec->objects[i]; + --exec->num_objects; + drm_gem_object_put(obj); + return; + } + + } +} +EXPORT_SYMBOL(drm_exec_unlock_obj); + +/** + * drm_exec_prepare_obj - prepare a GEM object for use + * @exec: the drm_exec object with the state + * @obj: the GEM object to prepare + * @num_fences: how many fences to reserve + * + * Prepare a GEM object for use by locking it and reserving fence slots. + * + * Returns: -EDEADLK if a contention is detected, -EALREADY when object is + * already locked, -ENOMEM when memory allocation failed and zero for success. + */ +int drm_exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj, + unsigned int num_fences) +{ + int ret; + + ret = drm_exec_lock_obj(exec, obj); + if (ret) + return ret; + + ret = dma_resv_reserve_fences(obj->resv, num_fences); + if (ret) { + drm_exec_unlock_obj(exec, obj); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(drm_exec_prepare_obj); + +/** + * drm_exec_prepare_array - helper to prepare an array of objects + * @exec: the drm_exec object with the state + * @objects: array of GEM object to prepare + * @num_objects: number of GEM objects in the array + * @num_fences: number of fences to reserve on each GEM object + * + * Prepares all GEM objects in an array, aborts on first error. + * Reserves @num_fences on each GEM object after locking it. + * + * Returns: -EDEADLOCK on contention, -EALREADY when object is already locked, + * -ENOMEM when memory allocation failed and zero for success. + */ +int drm_exec_prepare_array(struct drm_exec *exec, + struct drm_gem_object **objects, + unsigned int num_objects, + unsigned int num_fences) +{ + int ret; + + for (unsigned int i = 0; i < num_objects; ++i) { + ret = drm_exec_prepare_obj(exec, objects[i], num_fences); + if (unlikely(ret)) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(drm_exec_prepare_array); + +MODULE_DESCRIPTION("DRM execution context"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/drm_fbdev_dma.c b/drivers/gpu/drm/drm_fbdev_dma.c index f353daff65e1..73b8e99faad7 100644 --- a/drivers/gpu/drm/drm_fbdev_dma.c +++ b/drivers/gpu/drm/drm_fbdev_dma.c @@ -54,12 +54,8 @@ static void drm_fbdev_dma_fb_destroy(struct fb_info *info) static int drm_fbdev_dma_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; - - if (drm_WARN_ON_ONCE(dev, !fb_helper->dev->driver->gem_prime_mmap)) - return -ENODEV; - return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma); + return drm_gem_prime_mmap(fb_helper->buffer->gem, vma); } static const struct fb_ops drm_fbdev_dma_fb_ops = { diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 78dcae201cc6..75ef26d62c25 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1160,8 +1160,8 @@ int drm_gem_pin(struct drm_gem_object *obj) { if (obj->funcs->pin) return obj->funcs->pin(obj); - else - return 0; + + return 0; } void drm_gem_unpin(struct drm_gem_object *obj) diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 4ea6507a77e5..a783d2245599 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -88,8 +88,6 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private) if (ret) goto err_release; - mutex_init(&shmem->pages_lock); - mutex_init(&shmem->vmap_lock); INIT_LIST_HEAD(&shmem->madv_list); if (!private) { @@ -141,11 +139,13 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem) { struct drm_gem_object *obj = &shmem->base; - drm_WARN_ON(obj->dev, shmem->vmap_use_count); - if (obj->import_attach) { drm_prime_gem_destroy(obj, shmem->sgt); } else { + dma_resv_lock(shmem->base.resv, NULL); + + drm_WARN_ON(obj->dev, shmem->vmap_use_count); + if (shmem->sgt) { dma_unmap_sgtable(obj->dev->dev, shmem->sgt, DMA_BIDIRECTIONAL, 0); @@ -154,22 +154,24 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem) } if (shmem->pages) drm_gem_shmem_put_pages(shmem); - } - drm_WARN_ON(obj->dev, shmem->pages_use_count); + drm_WARN_ON(obj->dev, shmem->pages_use_count); + + dma_resv_unlock(shmem->base.resv); + } drm_gem_object_release(obj); - mutex_destroy(&shmem->pages_lock); - mutex_destroy(&shmem->vmap_lock); kfree(shmem); } EXPORT_SYMBOL_GPL(drm_gem_shmem_free); -static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem) +static int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem) { struct drm_gem_object *obj = &shmem->base; struct page **pages; + dma_resv_assert_held(shmem->base.resv); + if (shmem->pages_use_count++ > 0) return 0; @@ -197,35 +199,16 @@ static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem) } /* - * drm_gem_shmem_get_pages - Allocate backing pages for a shmem GEM object + * drm_gem_shmem_put_pages - Decrease use count on the backing pages for a shmem GEM object * @shmem: shmem GEM object * - * This function makes sure that backing pages exists for the shmem GEM object - * and increases the use count. - * - * Returns: - * 0 on success or a negative error code on failure. + * This function decreases the use count and puts the backing pages when use drops to zero. */ -int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem) +void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem) { struct drm_gem_object *obj = &shmem->base; - int ret; - drm_WARN_ON(obj->dev, obj->import_attach); - - ret = mutex_lock_interruptible(&shmem->pages_lock); - if (ret) - return ret; - ret = drm_gem_shmem_get_pages_locked(shmem); - mutex_unlock(&shmem->pages_lock); - - return ret; -} -EXPORT_SYMBOL(drm_gem_shmem_get_pages); - -static void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem) -{ - struct drm_gem_object *obj = &shmem->base; + dma_resv_assert_held(shmem->base.resv); if (drm_WARN_ON_ONCE(obj->dev, !shmem->pages_use_count)) return; @@ -243,20 +226,25 @@ static void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem) shmem->pages_mark_accessed_on_put); shmem->pages = NULL; } +EXPORT_SYMBOL(drm_gem_shmem_put_pages); -/* - * drm_gem_shmem_put_pages - Decrease use count on the backing pages for a shmem GEM object - * @shmem: shmem GEM object - * - * This function decreases the use count and puts the backing pages when use drops to zero. - */ -void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem) +static int drm_gem_shmem_pin_locked(struct drm_gem_shmem_object *shmem) { - mutex_lock(&shmem->pages_lock); - drm_gem_shmem_put_pages_locked(shmem); - mutex_unlock(&shmem->pages_lock); + int ret; + + dma_resv_assert_held(shmem->base.resv); + + ret = drm_gem_shmem_get_pages(shmem); + + return ret; +} + +static void drm_gem_shmem_unpin_locked(struct drm_gem_shmem_object *shmem) +{ + dma_resv_assert_held(shmem->base.resv); + + drm_gem_shmem_put_pages(shmem); } -EXPORT_SYMBOL(drm_gem_shmem_put_pages); /** * drm_gem_shmem_pin - Pin backing pages for a shmem GEM object @@ -271,10 +259,17 @@ EXPORT_SYMBOL(drm_gem_shmem_put_pages); int drm_gem_shmem_pin(struct drm_gem_shmem_object *shmem) { struct drm_gem_object *obj = &shmem->base; + int ret; drm_WARN_ON(obj->dev, obj->import_attach); - return drm_gem_shmem_get_pages(shmem); + ret = dma_resv_lock_interruptible(shmem->base.resv, NULL); + if (ret) + return ret; + ret = drm_gem_shmem_pin_locked(shmem); + dma_resv_unlock(shmem->base.resv); + + return ret; } EXPORT_SYMBOL(drm_gem_shmem_pin); @@ -291,12 +286,29 @@ void drm_gem_shmem_unpin(struct drm_gem_shmem_object *shmem) drm_WARN_ON(obj->dev, obj->import_attach); - drm_gem_shmem_put_pages(shmem); + dma_resv_lock(shmem->base.resv, NULL); + drm_gem_shmem_unpin_locked(shmem); + dma_resv_unlock(shmem->base.resv); } EXPORT_SYMBOL(drm_gem_shmem_unpin); -static int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem, - struct iosys_map *map) +/* + * drm_gem_shmem_vmap - Create a virtual mapping for a shmem GEM object + * @shmem: shmem GEM object + * @map: Returns the kernel virtual address of the SHMEM GEM object's backing + * store. + * + * This function makes sure that a contiguous kernel virtual address mapping + * exists for the buffer backing the shmem GEM object. It hides the differences + * between dma-buf imported and natively allocated objects. + * + * Acquired mappings should be cleaned up by calling drm_gem_shmem_vunmap(). + * + * Returns: + * 0 on success or a negative error code on failure. + */ +int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem, + struct iosys_map *map) { struct drm_gem_object *obj = &shmem->base; int ret = 0; @@ -312,6 +324,8 @@ static int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem, } else { pgprot_t prot = PAGE_KERNEL; + dma_resv_assert_held(shmem->base.resv); + if (shmem->vmap_use_count++ > 0) { iosys_map_set_vaddr(map, shmem->vaddr); return 0; @@ -346,45 +360,30 @@ err_zero_use: return ret; } +EXPORT_SYMBOL(drm_gem_shmem_vmap); /* - * drm_gem_shmem_vmap - Create a virtual mapping for a shmem GEM object + * drm_gem_shmem_vunmap - Unmap a virtual mapping for a shmem GEM object * @shmem: shmem GEM object - * @map: Returns the kernel virtual address of the SHMEM GEM object's backing - * store. - * - * This function makes sure that a contiguous kernel virtual address mapping - * exists for the buffer backing the shmem GEM object. It hides the differences - * between dma-buf imported and natively allocated objects. + * @map: Kernel virtual address where the SHMEM GEM object was mapped * - * Acquired mappings should be cleaned up by calling drm_gem_shmem_vunmap(). + * This function cleans up a kernel virtual address mapping acquired by + * drm_gem_shmem_vmap(). The mapping is only removed when the use count drops to + * zero. * - * Returns: - * 0 on success or a negative error code on failure. + * This function hides the differences between dma-buf imported and natively + * allocated objects. */ -int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem, - struct iosys_map *map) -{ - int ret; - - ret = mutex_lock_interruptible(&shmem->vmap_lock); - if (ret) - return ret; - ret = drm_gem_shmem_vmap_locked(shmem, map); - mutex_unlock(&shmem->vmap_lock); - - return ret; -} -EXPORT_SYMBOL(drm_gem_shmem_vmap); - -static void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem, - struct iosys_map *map) +void drm_gem_shmem_vunmap(struct drm_gem_shmem_object *shmem, + struct iosys_map *map) { struct drm_gem_object *obj = &shmem->base; if (obj->import_attach) { dma_buf_vunmap(obj->import_attach->dmabuf, map); } else { + dma_resv_assert_held(shmem->base.resv); + if (drm_WARN_ON_ONCE(obj->dev, !shmem->vmap_use_count)) return; @@ -397,26 +396,6 @@ static void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem, shmem->vaddr = NULL; } - -/* - * drm_gem_shmem_vunmap - Unmap a virtual mapping for a shmem GEM object - * @shmem: shmem GEM object - * @map: Kernel virtual address where the SHMEM GEM object was mapped - * - * This function cleans up a kernel virtual address mapping acquired by - * drm_gem_shmem_vmap(). The mapping is only removed when the use count drops to - * zero. - * - * This function hides the differences between dma-buf imported and natively - * allocated objects. - */ -void drm_gem_shmem_vunmap(struct drm_gem_shmem_object *shmem, - struct iosys_map *map) -{ - mutex_lock(&shmem->vmap_lock); - drm_gem_shmem_vunmap_locked(shmem, map); - mutex_unlock(&shmem->vmap_lock); -} EXPORT_SYMBOL(drm_gem_shmem_vunmap); static int @@ -447,24 +426,24 @@ drm_gem_shmem_create_with_handle(struct drm_file *file_priv, */ int drm_gem_shmem_madvise(struct drm_gem_shmem_object *shmem, int madv) { - mutex_lock(&shmem->pages_lock); + dma_resv_assert_held(shmem->base.resv); if (shmem->madv >= 0) shmem->madv = madv; madv = shmem->madv; - mutex_unlock(&shmem->pages_lock); - return (madv >= 0); } EXPORT_SYMBOL(drm_gem_shmem_madvise); -void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem) +void drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem) { struct drm_gem_object *obj = &shmem->base; struct drm_device *dev = obj->dev; + dma_resv_assert_held(shmem->base.resv); + drm_WARN_ON(obj->dev, !drm_gem_shmem_is_purgeable(shmem)); dma_unmap_sgtable(dev->dev, shmem->sgt, DMA_BIDIRECTIONAL, 0); @@ -472,7 +451,7 @@ void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem) kfree(shmem->sgt); shmem->sgt = NULL; - drm_gem_shmem_put_pages_locked(shmem); + drm_gem_shmem_put_pages(shmem); shmem->madv = -1; @@ -488,17 +467,6 @@ void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem) invalidate_mapping_pages(file_inode(obj->filp)->i_mapping, 0, (loff_t)-1); } -EXPORT_SYMBOL(drm_gem_shmem_purge_locked); - -bool drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem) -{ - if (!mutex_trylock(&shmem->pages_lock)) - return false; - drm_gem_shmem_purge_locked(shmem); - mutex_unlock(&shmem->pages_lock); - - return true; -} EXPORT_SYMBOL(drm_gem_shmem_purge); /** @@ -551,7 +519,7 @@ static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf) /* We don't use vmf->pgoff since that has the fake offset */ page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; - mutex_lock(&shmem->pages_lock); + dma_resv_lock(shmem->base.resv, NULL); if (page_offset >= num_pages || drm_WARN_ON_ONCE(obj->dev, !shmem->pages) || @@ -563,7 +531,7 @@ static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf) ret = vmf_insert_pfn(vma, vmf->address, page_to_pfn(page)); } - mutex_unlock(&shmem->pages_lock); + dma_resv_unlock(shmem->base.resv); return ret; } @@ -575,7 +543,7 @@ static void drm_gem_shmem_vm_open(struct vm_area_struct *vma) drm_WARN_ON(obj->dev, obj->import_attach); - mutex_lock(&shmem->pages_lock); + dma_resv_lock(shmem->base.resv, NULL); /* * We should have already pinned the pages when the buffer was first @@ -585,7 +553,7 @@ static void drm_gem_shmem_vm_open(struct vm_area_struct *vma) if (!drm_WARN_ON_ONCE(obj->dev, !shmem->pages_use_count)) shmem->pages_use_count++; - mutex_unlock(&shmem->pages_lock); + dma_resv_unlock(shmem->base.resv); drm_gem_vm_open(vma); } @@ -595,7 +563,10 @@ static void drm_gem_shmem_vm_close(struct vm_area_struct *vma) struct drm_gem_object *obj = vma->vm_private_data; struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); + dma_resv_lock(shmem->base.resv, NULL); drm_gem_shmem_put_pages(shmem); + dma_resv_unlock(shmem->base.resv); + drm_gem_vm_close(vma); } @@ -633,7 +604,10 @@ int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct return ret; } + dma_resv_lock(shmem->base.resv, NULL); ret = drm_gem_shmem_get_pages(shmem); + dma_resv_unlock(shmem->base.resv); + if (ret) return ret; @@ -699,7 +673,7 @@ static struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_ drm_WARN_ON(obj->dev, obj->import_attach); - ret = drm_gem_shmem_get_pages_locked(shmem); + ret = drm_gem_shmem_get_pages(shmem); if (ret) return ERR_PTR(ret); @@ -721,7 +695,7 @@ err_free_sgt: sg_free_table(sgt); kfree(sgt); err_put_pages: - drm_gem_shmem_put_pages_locked(shmem); + drm_gem_shmem_put_pages(shmem); return ERR_PTR(ret); } @@ -746,11 +720,11 @@ struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_shmem_object *shmem) int ret; struct sg_table *sgt; - ret = mutex_lock_interruptible(&shmem->pages_lock); + ret = dma_resv_lock_interruptible(shmem->base.resv, NULL); if (ret) return ERR_PTR(ret); sgt = drm_gem_shmem_get_pages_sgt_locked(shmem); - mutex_unlock(&shmem->pages_lock); + dma_resv_unlock(shmem->base.resv); return sgt; } diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 7c9d66ee917d..8e9afe7af19c 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -245,8 +245,7 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_ req->value = 1; return 0; case DRM_CAP_PRIME: - req->value |= dev->driver->prime_fd_to_handle ? DRM_PRIME_CAP_IMPORT : 0; - req->value |= dev->driver->prime_handle_to_fd ? DRM_PRIME_CAP_EXPORT : 0; + req->value = DRM_PRIME_CAP_IMPORT | DRM_PRIME_CAP_EXPORT; return 0; case DRM_CAP_SYNCOBJ: req->value = drm_core_check_feature(dev, DRIVER_SYNCOBJ); diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c index ba1608effc0f..ac0d2ce3f870 100644 --- a/drivers/gpu/drm/drm_mode_object.c +++ b/drivers/gpu/drm/drm_mode_object.c @@ -147,8 +147,10 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, obj = NULL; if (obj && drm_mode_object_lease_required(obj->type) && - !_drm_lease_held(file_priv, obj->id)) + !_drm_lease_held(file_priv, obj->id)) { + drm_dbg_kms(dev, "[OBJECT:%d] not included in lease", id); obj = NULL; + } if (obj && obj->free_cb) { if (!kref_get_unless_zero(&obj->refcount)) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index d29dafce9bb0..63b709a67471 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -51,15 +51,10 @@ MODULE_IMPORT_NS(DMA_BUF); * between applications, they can't be guessed like the globally unique GEM * names. * - * Drivers that support the PRIME API implement the - * &drm_driver.prime_handle_to_fd and &drm_driver.prime_fd_to_handle operations. - * GEM based drivers must use drm_gem_prime_handle_to_fd() and - * drm_gem_prime_fd_to_handle() to implement these. For GEM based drivers the - * actual driver interfaces is provided through the &drm_gem_object_funcs.export - * and &drm_driver.gem_prime_import hooks. - * - * &dma_buf_ops implementations for GEM drivers are all individually exported - * for drivers which need to overwrite or reimplement some of them. + * Drivers that support the PRIME API implement the drm_gem_object_funcs.export + * and &drm_driver.gem_prime_import hooks. &dma_buf_ops implementations for + * drivers are all individually exported for drivers which need to overwrite + * or reimplement some of them. * * Reference Counting for GEM Drivers * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -283,7 +278,7 @@ void drm_gem_dmabuf_release(struct dma_buf *dma_buf) } EXPORT_SYMBOL(drm_gem_dmabuf_release); -/** +/* * drm_gem_prime_fd_to_handle - PRIME import function for GEM drivers * @dev: drm_device to import into * @file_priv: drm file-private structure @@ -297,9 +292,9 @@ EXPORT_SYMBOL(drm_gem_dmabuf_release); * * Returns 0 on success or a negative error code on failure. */ -int drm_gem_prime_fd_to_handle(struct drm_device *dev, - struct drm_file *file_priv, int prime_fd, - uint32_t *handle) +static int drm_gem_prime_fd_to_handle(struct drm_device *dev, + struct drm_file *file_priv, int prime_fd, + uint32_t *handle) { struct dma_buf *dma_buf; struct drm_gem_object *obj; @@ -365,18 +360,18 @@ out_put: dma_buf_put(dma_buf); return ret; } -EXPORT_SYMBOL(drm_gem_prime_fd_to_handle); int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_prime_handle *args = data; - if (!dev->driver->prime_fd_to_handle) - return -ENOSYS; + if (dev->driver->prime_fd_to_handle) { + return dev->driver->prime_fd_to_handle(dev, file_priv, args->fd, + &args->handle); + } - return dev->driver->prime_fd_to_handle(dev, file_priv, - args->fd, &args->handle); + return drm_gem_prime_fd_to_handle(dev, file_priv, args->fd, &args->handle); } static struct dma_buf *export_and_register_object(struct drm_device *dev, @@ -413,7 +408,7 @@ static struct dma_buf *export_and_register_object(struct drm_device *dev, return dmabuf; } -/** +/* * drm_gem_prime_handle_to_fd - PRIME export function for GEM drivers * @dev: dev to export the buffer from * @file_priv: drm file-private structure @@ -426,10 +421,10 @@ static struct dma_buf *export_and_register_object(struct drm_device *dev, * The actual exporting from GEM object to a dma-buf is done through the * &drm_gem_object_funcs.export callback. */ -int drm_gem_prime_handle_to_fd(struct drm_device *dev, - struct drm_file *file_priv, uint32_t handle, - uint32_t flags, - int *prime_fd) +static int drm_gem_prime_handle_to_fd(struct drm_device *dev, + struct drm_file *file_priv, uint32_t handle, + uint32_t flags, + int *prime_fd) { struct drm_gem_object *obj; int ret = 0; @@ -511,22 +506,23 @@ out_unlock: return ret; } -EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_prime_handle *args = data; - if (!dev->driver->prime_handle_to_fd) - return -ENOSYS; - /* check flags are valid */ if (args->flags & ~(DRM_CLOEXEC | DRM_RDWR)) return -EINVAL; - return dev->driver->prime_handle_to_fd(dev, file_priv, - args->handle, args->flags, &args->fd); + if (dev->driver->prime_handle_to_fd) { + return dev->driver->prime_handle_to_fd(dev, file_priv, + args->handle, args->flags, + &args->fd); + } + return drm_gem_prime_handle_to_fd(dev, file_priv, args->handle, + args->flags, &args->fd); } /** @@ -715,8 +711,6 @@ EXPORT_SYMBOL(drm_gem_dmabuf_vunmap); * the same codepath that is used for regular GEM buffer mapping on the DRM fd. * The fake GEM offset is added to vma->vm_pgoff and &drm_driver->fops->mmap is * called to set up the mapping. - * - * Drivers can use this as their &drm_driver.gem_prime_mmap callback. */ int drm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) { @@ -772,25 +766,15 @@ EXPORT_SYMBOL(drm_gem_prime_mmap); * @vma: virtual address range * * Provides memory mapping for the buffer. This can be used as the - * &dma_buf_ops.mmap callback. It just forwards to &drm_driver.gem_prime_mmap, - * which should be set to drm_gem_prime_mmap(). - * - * FIXME: There's really no point to this wrapper, drivers which need anything - * else but drm_gem_prime_mmap can roll their own &dma_buf_ops.mmap callback. + * &dma_buf_ops.mmap callback. It just forwards to drm_gem_prime_mmap(). * * Returns 0 on success or a negative error code on failure. */ int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) { struct drm_gem_object *obj = dma_buf->priv; - struct drm_device *dev = obj->dev; - - dma_resv_assert_held(dma_buf->resv); - - if (!dev->driver->gem_prime_mmap) - return -ENOSYS; - return dev->driver->gem_prime_mmap(obj, vma); + return drm_gem_prime_mmap(obj, vma); } EXPORT_SYMBOL(drm_gem_dmabuf_mmap); @@ -880,9 +864,9 @@ EXPORT_SYMBOL(drm_prime_get_contiguous_size); * @obj: GEM object to export * @flags: flags like DRM_CLOEXEC and DRM_RDWR * - * This is the implementation of the &drm_gem_object_funcs.export functions for GEM drivers - * using the PRIME helpers. It is used as the default in - * drm_gem_prime_handle_to_fd(). + * This is the implementation of the &drm_gem_object_funcs.export functions + * for GEM drivers using the PRIME helpers. It is used as the default for + * drivers that do not set their own. */ struct dma_buf *drm_gem_prime_export(struct drm_gem_object *obj, int flags) @@ -978,10 +962,9 @@ EXPORT_SYMBOL(drm_gem_prime_import_dev); * @dev: drm_device to import into * @dma_buf: dma-buf object to import * - * This is the implementation of the gem_prime_import functions for GEM drivers - * using the PRIME helpers. Drivers can use this as their - * &drm_driver.gem_prime_import implementation. It is used as the default - * implementation in drm_gem_prime_fd_to_handle(). + * This is the implementation of the gem_prime_import functions for GEM + * drivers using the PRIME helpers. It is the default for drivers that do + * not set their own &drm_driver.gem_prime_import. * * Drivers must arrange to call drm_prime_gem_destroy() from their * &drm_gem_object_funcs.free hook when using this function. diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index f62767ff34b2..b169b3e44a92 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -487,17 +487,17 @@ void drm_sysfs_connector_hotplug_event(struct drm_connector *connector) EXPORT_SYMBOL(drm_sysfs_connector_hotplug_event); /** - * drm_sysfs_connector_status_event - generate a DRM uevent for connector - * property status change - * @connector: connector on which property status changed - * @property: connector property whose status changed. + * drm_sysfs_connector_property_event - generate a DRM uevent for connector + * property change + * @connector: connector on which property changed + * @property: connector property which has changed. * - * Send a uevent for the DRM device specified by @dev. Currently we + * Send a uevent for the specified DRM connector and property. Currently we * set HOTPLUG=1 and connector id along with the attached property id - * related to the status change. + * related to the change. */ -void drm_sysfs_connector_status_event(struct drm_connector *connector, - struct drm_property *property) +void drm_sysfs_connector_property_event(struct drm_connector *connector, + struct drm_property *property) { struct drm_device *dev = connector->dev; char hotplug_str[] = "HOTPLUG=1", conn_id[21], prop_id[21]; @@ -511,11 +511,14 @@ void drm_sysfs_connector_status_event(struct drm_connector *connector, snprintf(prop_id, ARRAY_SIZE(prop_id), "PROPERTY=%u", property->base.id); - DRM_DEBUG("generating connector status event\n"); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] generating connector property event for [PROP:%d:%s]\n", + connector->base.id, connector->name, + property->base.id, property->name); kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp); } -EXPORT_SYMBOL(drm_sysfs_connector_status_event); +EXPORT_SYMBOL(drm_sysfs_connector_property_event); struct device *drm_sysfs_minor_alloc(struct drm_minor *minor) { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 31a7f59ccb49..ea55f6b7b744 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -481,10 +481,7 @@ static const struct drm_driver etnaviv_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_RENDER, .open = etnaviv_open, .postclose = etnaviv_postclose, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = etnaviv_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, #ifdef CONFIG_DEBUG_FS .debugfs_init = etnaviv_debugfs_init, #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 6b73fb7a83c3..8399256cb5c9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -109,11 +109,8 @@ static const struct drm_driver exynos_drm_driver = { .open = exynos_drm_open, .postclose = exynos_drm_postclose, .dumb_create = exynos_drm_gem_dumb_create, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = exynos_drm_gem_prime_import, .gem_prime_import_sg_table = exynos_drm_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .ioctls = exynos_ioctls, .num_ioctls = ARRAY_SIZE(exynos_ioctls), .fops = &exynos_drm_driver_fops, diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index c09ba019ba5e..a395f93449f3 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -346,7 +346,7 @@ disable_clk: return ret; } -static int fsl_dcu_drm_remove(struct platform_device *pdev) +static void fsl_dcu_drm_remove(struct platform_device *pdev) { struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev); @@ -354,13 +354,11 @@ static int fsl_dcu_drm_remove(struct platform_device *pdev) drm_dev_put(fsl_dev->drm); clk_disable_unprepare(fsl_dev->clk); clk_unregister(fsl_dev->pix_clk); - - return 0; } static struct platform_driver fsl_dcu_drm_platform_driver = { .probe = fsl_dcu_drm_probe, - .remove = fsl_dcu_drm_remove, + .remove_new = fsl_dcu_drm_remove, .driver = { .name = "fsl-dcu", .pm = &fsl_dcu_drm_pm_ops, diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c index dc16a92625d4..d2f199ea3c11 100644 --- a/drivers/gpu/drm/gud/gud_pipe.c +++ b/drivers/gpu/drm/gud/gud_pipe.c @@ -390,7 +390,7 @@ static int gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer * mutex_lock(&gdrm->damage_lock); if (!gdrm->shadow_buf) { - gdrm->shadow_buf = vzalloc(fb->pitches[0] * fb->height); + gdrm->shadow_buf = vcalloc(fb->pitches[0], fb->height); if (!gdrm->shadow_buf) { mutex_unlock(&gdrm->damage_lock); return -ENOMEM; diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 0c4aa4d9b0a7..8a98fa276e8a 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -63,7 +63,6 @@ static const struct drm_driver hibmc_driver = { .debugfs_init = drm_vram_mm_debugfs_init, .dumb_create = hibmc_dumb_create, .dumb_map_offset = drm_gem_ttm_dumb_map_offset, - .gem_prime_mmap = drm_gem_prime_mmap, }; static int __maybe_unused hibmc_pm_suspend(struct device *dev) diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index d9978b79828c..566de4658719 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -874,14 +874,12 @@ static int dsi_probe(struct platform_device *pdev) return 0; } -static int dsi_remove(struct platform_device *pdev) +static void dsi_remove(struct platform_device *pdev) { struct dsi_data *data = platform_get_drvdata(pdev); struct dw_dsi *dsi = &data->dsi; mipi_dsi_host_unregister(&dsi->host); - - return 0; } static const struct of_device_id dsi_of_match[] = { @@ -892,7 +890,7 @@ MODULE_DEVICE_TABLE(of, dsi_of_match); static struct platform_driver dsi_driver = { .probe = dsi_probe, - .remove = dsi_remove, + .remove_new = dsi_remove, .driver = { .name = "dw-dsi", .of_match_table = dsi_of_match, diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 9c5d49bf40c9..bc2948500311 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -279,10 +279,9 @@ static int kirin_drm_platform_probe(struct platform_device *pdev) return component_master_add_with_match(dev, &kirin_drm_ops, match); } -static int kirin_drm_platform_remove(struct platform_device *pdev) +static void kirin_drm_platform_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &kirin_drm_ops); - return 0; } static const struct of_device_id kirin_drm_dt_ids[] = { @@ -295,7 +294,7 @@ MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids); static struct platform_driver kirin_drm_platform_driver = { .probe = kirin_drm_platform_probe, - .remove = kirin_drm_platform_remove, + .remove_new = kirin_drm_platform_remove, .driver = { .name = "kirin-drm", .of_match_table = kirin_drm_dt_ids, diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c index a7d2c92d6c6a..8026118c6e03 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c @@ -7,6 +7,7 @@ #include <linux/hyperv.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/screen_info.h> #include <drm/drm_aperture.h> #include <drm/drm_atomic_helper.h> diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index 521bdf656cca..131512a5f3bd 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -497,7 +497,7 @@ static const struct dev_pm_ops ch7006_pm_ops = { static struct drm_i2c_encoder_driver ch7006_driver = { .i2c_driver = { - .probe_new = ch7006_probe, + .probe = ch7006_probe, .remove = ch7006_remove, .driver = { diff --git a/drivers/gpu/drm/i2c/sil164_drv.c b/drivers/gpu/drm/i2c/sil164_drv.c index f57f9a807542..ff23422727fc 100644 --- a/drivers/gpu/drm/i2c/sil164_drv.c +++ b/drivers/gpu/drm/i2c/sil164_drv.c @@ -420,7 +420,7 @@ MODULE_DEVICE_TABLE(i2c, sil164_ids); static struct drm_i2c_encoder_driver sil164_driver = { .i2c_driver = { - .probe_new = sil164_probe, + .probe = sil164_probe, .driver = { .name = "sil164", }, diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index fd556a076d05..1df74f7aa3dc 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -97,8 +97,6 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct * struct drm_i915_private *i915 = to_i915(obj->base.dev); int ret; - dma_resv_assert_held(dma_buf->resv); - if (obj->base.size < vma->vm_end - vma->vm_start) return -EINVAL; diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 0ad0c5885ec2..222d0a1f3b55 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -1818,8 +1818,6 @@ static const struct drm_driver i915_drm_driver = { .postclose = i915_driver_postclose, .show_fdinfo = PTR_IF(IS_ENABLED(CONFIG_PROC_FS), i915_drm_client_fdinfo), - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = i915_gem_prime_import, .dumb_create = i915_gem_dumb_create, diff --git a/drivers/gpu/drm/ingenic/Kconfig b/drivers/gpu/drm/ingenic/Kconfig index a53f475d33df..b440e0cdc057 100644 --- a/drivers/gpu/drm/ingenic/Kconfig +++ b/drivers/gpu/drm/ingenic/Kconfig @@ -9,6 +9,8 @@ config DRM_INGENIC select DRM_PANEL_BRIDGE select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER + select REGMAP + select REGMAP_MMIO select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE help Choose this option for DRM support for the Ingenic SoCs. diff --git a/drivers/gpu/drm/lima/lima_drv.c b/drivers/gpu/drm/lima/lima_drv.c index 39cab4a55f57..3dd078f443bb 100644 --- a/drivers/gpu/drm/lima/lima_drv.c +++ b/drivers/gpu/drm/lima/lima_drv.c @@ -276,10 +276,7 @@ static const struct drm_driver lima_drm_driver = { .patchlevel = 0, .gem_create_object = lima_gem_create_object, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .gem_prime_mmap = drm_gem_prime_mmap, }; struct lima_block_reader { @@ -441,7 +438,7 @@ err_out0: return err; } -static int lima_pdev_remove(struct platform_device *pdev) +static void lima_pdev_remove(struct platform_device *pdev) { struct lima_device *ldev = platform_get_drvdata(pdev); struct drm_device *ddev = ldev->ddev; @@ -459,7 +456,6 @@ static int lima_pdev_remove(struct platform_device *pdev) drm_dev_put(ddev); lima_sched_slab_fini(); - return 0; } static const struct of_device_id dt_match[] = { @@ -476,7 +472,7 @@ static const struct dev_pm_ops lima_pm_ops = { static struct platform_driver lima_platform_driver = { .probe = lima_pdev_probe, - .remove = lima_pdev_remove, + .remove_new = lima_pdev_remove, .driver = { .name = "lima", .pm = &lima_pm_ops, diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c index 10252dc11a22..4f9736e5f929 100644 --- a/drivers/gpu/drm/lima/lima_gem.c +++ b/drivers/gpu/drm/lima/lima_gem.c @@ -34,7 +34,7 @@ int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm) new_size = min(new_size, bo->base.base.size); - mutex_lock(&bo->base.pages_lock); + dma_resv_lock(bo->base.base.resv, NULL); if (bo->base.pages) { pages = bo->base.pages; @@ -42,7 +42,7 @@ int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm) pages = kvmalloc_array(bo->base.base.size >> PAGE_SHIFT, sizeof(*pages), GFP_KERNEL | __GFP_ZERO); if (!pages) { - mutex_unlock(&bo->base.pages_lock); + dma_resv_unlock(bo->base.base.resv); return -ENOMEM; } @@ -56,13 +56,13 @@ int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm) struct page *page = shmem_read_mapping_page(mapping, i); if (IS_ERR(page)) { - mutex_unlock(&bo->base.pages_lock); + dma_resv_unlock(bo->base.base.resv); return PTR_ERR(page); } pages[i] = page; } - mutex_unlock(&bo->base.pages_lock); + dma_resv_unlock(bo->base.base.resv); ret = sg_alloc_table_from_pages(&sgt, pages, i, 0, new_size, GFP_KERNEL); diff --git a/drivers/gpu/drm/logicvc/logicvc_drm.c b/drivers/gpu/drm/logicvc/logicvc_drm.c index c35c453fd025..c8639c8a868d 100644 --- a/drivers/gpu/drm/logicvc/logicvc_drm.c +++ b/drivers/gpu/drm/logicvc/logicvc_drm.c @@ -466,7 +466,7 @@ error_early: return ret; } -static int logicvc_drm_remove(struct platform_device *pdev) +static void logicvc_drm_remove(struct platform_device *pdev) { struct logicvc_drm *logicvc = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; @@ -480,8 +480,6 @@ static int logicvc_drm_remove(struct platform_device *pdev) logicvc_clocks_unprepare(logicvc); of_reserved_mem_device_release(dev); - - return 0; } static const struct of_device_id logicvc_drm_of_table[] = { @@ -493,7 +491,7 @@ MODULE_DEVICE_TABLE(of, logicvc_drm_of_table); static struct platform_driver logicvc_drm_platform_driver = { .probe = logicvc_drm_probe, - .remove = logicvc_drm_remove, + .remove_new = logicvc_drm_remove, .driver = { .name = "logicvc-drm", .of_match_table = logicvc_drm_of_table, diff --git a/drivers/gpu/drm/loongson/Kconfig b/drivers/gpu/drm/loongson/Kconfig new file mode 100644 index 000000000000..df6946d505fa --- /dev/null +++ b/drivers/gpu/drm/loongson/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 + +config DRM_LOONGSON + tristate "DRM support for Loongson Graphics" + depends on DRM && PCI && MMU + select DRM_KMS_HELPER + select DRM_TTM + select I2C + select I2C_ALGOBIT + help + This is a DRM driver for Loongson Graphics, it may including + LS7A2000, LS7A1000, LS2K2000 and LS2K1000 etc. Loongson LS7A + series are bridge chipset, while Loongson LS2K series are SoC. + + If "M" is selected, the module will be called loongson. + + If in doubt, say "N". diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile new file mode 100644 index 000000000000..91e72bd900c1 --- /dev/null +++ b/drivers/gpu/drm/loongson/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 + +loongson-y := \ + lsdc_benchmark.o \ + lsdc_crtc.o \ + lsdc_debugfs.o \ + lsdc_drv.o \ + lsdc_gem.o \ + lsdc_gfxpll.o \ + lsdc_i2c.o \ + lsdc_irq.o \ + lsdc_output_7a1000.o \ + lsdc_output_7a2000.o \ + lsdc_plane.o \ + lsdc_pixpll.o \ + lsdc_probe.o \ + lsdc_ttm.o + +loongson-y += loongson_device.o \ + loongson_module.o + +obj-$(CONFIG_DRM_LOONGSON) += loongson.o diff --git a/drivers/gpu/drm/loongson/loongson_device.c b/drivers/gpu/drm/loongson/loongson_device.c new file mode 100644 index 000000000000..9986c8a2a255 --- /dev/null +++ b/drivers/gpu/drm/loongson/loongson_device.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/pci.h> + +#include "lsdc_drv.h" + +static const struct lsdc_kms_funcs ls7a1000_kms_funcs = { + .create_i2c = lsdc_create_i2c_chan, + .irq_handler = ls7a1000_dc_irq_handler, + .output_init = ls7a1000_output_init, + .cursor_plane_init = ls7a1000_cursor_plane_init, + .primary_plane_init = lsdc_primary_plane_init, + .crtc_init = ls7a1000_crtc_init, +}; + +static const struct lsdc_kms_funcs ls7a2000_kms_funcs = { + .create_i2c = lsdc_create_i2c_chan, + .irq_handler = ls7a2000_dc_irq_handler, + .output_init = ls7a2000_output_init, + .cursor_plane_init = ls7a2000_cursor_plane_init, + .primary_plane_init = lsdc_primary_plane_init, + .crtc_init = ls7a2000_crtc_init, +}; + +static const struct loongson_gfx_desc ls7a1000_gfx = { + .dc = { + .num_of_crtc = 2, + .max_pixel_clk = 200000, + .max_width = 2048, + .max_height = 2048, + .num_of_hw_cursor = 1, + .hw_cursor_w = 32, + .hw_cursor_h = 32, + .pitch_align = 256, + .has_vblank_counter = false, + .funcs = &ls7a1000_kms_funcs, + }, + .conf_reg_base = LS7A1000_CONF_REG_BASE, + .gfxpll = { + .reg_offset = LS7A1000_PLL_GFX_REG, + .reg_size = 8, + }, + .pixpll = { + [0] = { + .reg_offset = LS7A1000_PIXPLL0_REG, + .reg_size = 8, + }, + [1] = { + .reg_offset = LS7A1000_PIXPLL1_REG, + .reg_size = 8, + }, + }, + .chip_id = CHIP_LS7A1000, + .model = "LS7A1000 bridge chipset", +}; + +static const struct loongson_gfx_desc ls7a2000_gfx = { + .dc = { + .num_of_crtc = 2, + .max_pixel_clk = 350000, + .max_width = 4096, + .max_height = 4096, + .num_of_hw_cursor = 2, + .hw_cursor_w = 64, + .hw_cursor_h = 64, + .pitch_align = 64, + .has_vblank_counter = true, + .funcs = &ls7a2000_kms_funcs, + }, + .conf_reg_base = LS7A2000_CONF_REG_BASE, + .gfxpll = { + .reg_offset = LS7A2000_PLL_GFX_REG, + .reg_size = 8, + }, + .pixpll = { + [0] = { + .reg_offset = LS7A2000_PIXPLL0_REG, + .reg_size = 8, + }, + [1] = { + .reg_offset = LS7A2000_PIXPLL1_REG, + .reg_size = 8, + }, + }, + .chip_id = CHIP_LS7A2000, + .model = "LS7A2000 bridge chipset", +}; + +static const struct lsdc_desc *__chip_id_desc_table[] = { + [CHIP_LS7A1000] = &ls7a1000_gfx.dc, + [CHIP_LS7A2000] = &ls7a2000_gfx.dc, + [CHIP_LS_LAST] = NULL, +}; + +const struct lsdc_desc * +lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip_id) +{ + return __chip_id_desc_table[chip_id]; +} diff --git a/drivers/gpu/drm/loongson/loongson_module.c b/drivers/gpu/drm/loongson/loongson_module.c new file mode 100644 index 000000000000..d2a51bd395f6 --- /dev/null +++ b/drivers/gpu/drm/loongson/loongson_module.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/pci.h> + +#include <video/nomodeset.h> + +#include "loongson_module.h" + +static int loongson_modeset = -1; +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); +module_param_named(modeset, loongson_modeset, int, 0400); + +int loongson_vblank = 1; +MODULE_PARM_DESC(vblank, "Disable/Enable hw vblank support"); +module_param_named(vblank, loongson_vblank, int, 0400); + +static int __init loongson_module_init(void) +{ + if (!loongson_modeset || video_firmware_drivers_only()) + return -ENODEV; + + return pci_register_driver(&lsdc_pci_driver); +} +module_init(loongson_module_init); + +static void __exit loongson_module_exit(void) +{ + pci_unregister_driver(&lsdc_pci_driver); +} +module_exit(loongson_module_exit); diff --git a/drivers/gpu/drm/loongson/loongson_module.h b/drivers/gpu/drm/loongson/loongson_module.h new file mode 100644 index 000000000000..931c17521bf0 --- /dev/null +++ b/drivers/gpu/drm/loongson/loongson_module.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LOONGSON_MODULE_H__ +#define __LOONGSON_MODULE_H__ + +extern int loongson_vblank; +extern struct pci_driver lsdc_pci_driver; + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_benchmark.c b/drivers/gpu/drm/loongson/lsdc_benchmark.c new file mode 100644 index 000000000000..b088646a2ff9 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_benchmark.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <drm/drm_debugfs.h> + +#include "lsdc_benchmark.h" +#include "lsdc_drv.h" +#include "lsdc_gem.h" +#include "lsdc_ttm.h" + +typedef void (*lsdc_copy_proc_t)(struct lsdc_bo *src_bo, + struct lsdc_bo *dst_bo, + unsigned int size, + int n); + +static void lsdc_copy_gtt_to_vram_cpu(struct lsdc_bo *src_bo, + struct lsdc_bo *dst_bo, + unsigned int size, + int n) +{ + lsdc_bo_kmap(src_bo); + lsdc_bo_kmap(dst_bo); + + while (n--) + memcpy_toio(dst_bo->kptr, src_bo->kptr, size); + + lsdc_bo_kunmap(src_bo); + lsdc_bo_kunmap(dst_bo); +} + +static void lsdc_copy_vram_to_gtt_cpu(struct lsdc_bo *src_bo, + struct lsdc_bo *dst_bo, + unsigned int size, + int n) +{ + lsdc_bo_kmap(src_bo); + lsdc_bo_kmap(dst_bo); + + while (n--) + memcpy_fromio(dst_bo->kptr, src_bo->kptr, size); + + lsdc_bo_kunmap(src_bo); + lsdc_bo_kunmap(dst_bo); +} + +static void lsdc_copy_gtt_to_gtt_cpu(struct lsdc_bo *src_bo, + struct lsdc_bo *dst_bo, + unsigned int size, + int n) +{ + lsdc_bo_kmap(src_bo); + lsdc_bo_kmap(dst_bo); + + while (n--) + memcpy(dst_bo->kptr, src_bo->kptr, size); + + lsdc_bo_kunmap(src_bo); + lsdc_bo_kunmap(dst_bo); +} + +static void lsdc_benchmark_copy(struct lsdc_device *ldev, + unsigned int size, + unsigned int n, + u32 src_domain, + u32 dst_domain, + lsdc_copy_proc_t copy_proc, + struct drm_printer *p) +{ + struct drm_device *ddev = &ldev->base; + struct lsdc_bo *src_bo; + struct lsdc_bo *dst_bo; + unsigned long start_jiffies; + unsigned long end_jiffies; + unsigned int throughput; + unsigned int time; + + src_bo = lsdc_bo_create_kernel_pinned(ddev, src_domain, size); + dst_bo = lsdc_bo_create_kernel_pinned(ddev, dst_domain, size); + + start_jiffies = jiffies; + + copy_proc(src_bo, dst_bo, size, n); + + end_jiffies = jiffies; + + lsdc_bo_free_kernel_pinned(src_bo); + lsdc_bo_free_kernel_pinned(dst_bo); + + time = jiffies_to_msecs(end_jiffies - start_jiffies); + + throughput = (n * (size >> 10)) / time; + + drm_printf(p, + "Copy bo of %uKiB %u times from %s to %s in %ums: %uMB/s\n", + size >> 10, n, + lsdc_domain_to_str(src_domain), + lsdc_domain_to_str(dst_domain), + time, throughput); +} + +int lsdc_show_benchmark_copy(struct lsdc_device *ldev, struct drm_printer *p) +{ + unsigned int buffer_size = 1920 * 1080 * 4; + unsigned int iteration = 60; + + lsdc_benchmark_copy(ldev, + buffer_size, + iteration, + LSDC_GEM_DOMAIN_GTT, + LSDC_GEM_DOMAIN_GTT, + lsdc_copy_gtt_to_gtt_cpu, + p); + + lsdc_benchmark_copy(ldev, + buffer_size, + iteration, + LSDC_GEM_DOMAIN_GTT, + LSDC_GEM_DOMAIN_VRAM, + lsdc_copy_gtt_to_vram_cpu, + p); + + lsdc_benchmark_copy(ldev, + buffer_size, + iteration, + LSDC_GEM_DOMAIN_VRAM, + LSDC_GEM_DOMAIN_GTT, + lsdc_copy_vram_to_gtt_cpu, + p); + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_benchmark.h b/drivers/gpu/drm/loongson/lsdc_benchmark.h new file mode 100644 index 000000000000..36110278237e --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_benchmark.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_BENCHMARK_H__ +#define __LSDC_BENCHMARK_H__ + +#include "lsdc_drv.h" + +int lsdc_show_benchmark_copy(struct lsdc_device *ldev, struct drm_printer *p); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_crtc.c b/drivers/gpu/drm/loongson/lsdc_crtc.c new file mode 100644 index 000000000000..827acab580fa --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_crtc.c @@ -0,0 +1,1024 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/delay.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_debugfs.h> +#include <drm/drm_vblank.h> + +#include "lsdc_drv.h" + +/* + * After the CRTC soft reset, the vblank counter would be reset to zero. + * But the address and other settings in the CRTC register remain the same + * as before. + */ + +static void lsdc_crtc0_soft_reset(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + u32 val; + + val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); + + val &= CFG_VALID_BITS_MASK; + + /* Soft reset bit, active low */ + val &= ~CFG_RESET_N; + + val &= ~CFG_PIX_FMT_MASK; + + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val); + + udelay(1); + + val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE; + + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val); + + /* Wait about a vblank time */ + mdelay(20); +} + +static void lsdc_crtc1_soft_reset(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + u32 val; + + val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); + + val &= CFG_VALID_BITS_MASK; + + /* Soft reset bit, active low */ + val &= ~CFG_RESET_N; + + val &= ~CFG_PIX_FMT_MASK; + + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val); + + udelay(1); + + val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE; + + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val); + + /* Wait about a vblank time */ + msleep(20); +} + +static void lsdc_crtc0_enable(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + u32 val; + + val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); + + /* + * This may happen in extremely rare cases, but a soft reset can + * bring it back to normal. We add a warning here, hoping to catch + * something if it happens. + */ + if (val & CRTC_ANCHORED) { + drm_warn(&ldev->base, "%s stall\n", lcrtc->base.name); + return lsdc_crtc0_soft_reset(lcrtc); + } + + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val | CFG_OUTPUT_ENABLE); +} + +static void lsdc_crtc0_disable(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_ureg32_clr(ldev, LSDC_CRTC0_CFG_REG, CFG_OUTPUT_ENABLE); + + udelay(9); +} + +static void lsdc_crtc1_enable(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + u32 val; + + /* + * This may happen in extremely rare cases, but a soft reset can + * bring it back to normal. We add a warning here, hoping to catch + * something if it happens. + */ + val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); + if (val & CRTC_ANCHORED) { + drm_warn(&ldev->base, "%s stall\n", lcrtc->base.name); + return lsdc_crtc1_soft_reset(lcrtc); + } + + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val | CFG_OUTPUT_ENABLE); +} + +static void lsdc_crtc1_disable(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_ureg32_clr(ldev, LSDC_CRTC1_CFG_REG, CFG_OUTPUT_ENABLE); + + udelay(9); +} + +/* All Loongson display controllers have hardware scanout position recoders */ + +static void lsdc_crtc0_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos) +{ + struct lsdc_device *ldev = lcrtc->ldev; + u32 val; + + val = lsdc_rreg32(ldev, LSDC_CRTC0_SCAN_POS_REG); + + *hpos = val >> 16; + *vpos = val & 0xffff; +} + +static void lsdc_crtc1_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos) +{ + struct lsdc_device *ldev = lcrtc->ldev; + u32 val; + + val = lsdc_rreg32(ldev, LSDC_CRTC1_SCAN_POS_REG); + + *hpos = val >> 16; + *vpos = val & 0xffff; +} + +static void lsdc_crtc0_enable_vblank(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN); +} + +static void lsdc_crtc0_disable_vblank(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN); +} + +static void lsdc_crtc1_enable_vblank(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN); +} + +static void lsdc_crtc1_disable_vblank(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN); +} + +static void lsdc_crtc0_flip(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_PAGE_FLIP); +} + +static void lsdc_crtc1_flip(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_PAGE_FLIP); +} + +/* + * CRTC0 clone from CRTC1 or CRTC1 clone from CRTC0 using hardware logic + * This may be useful for custom cloning (TWIN) applications. Saving the + * bandwidth compared with the clone (mirroring) display mode provided by + * drm core. + */ + +static void lsdc_crtc0_clone(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_HW_CLONE); +} + +static void lsdc_crtc1_clone(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_HW_CLONE); +} + +static void lsdc_crtc0_set_mode(struct lsdc_crtc *lcrtc, + const struct drm_display_mode *mode) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_wreg32(ldev, LSDC_CRTC0_HDISPLAY_REG, + (mode->crtc_htotal << 16) | mode->crtc_hdisplay); + + lsdc_wreg32(ldev, LSDC_CRTC0_VDISPLAY_REG, + (mode->crtc_vtotal << 16) | mode->crtc_vdisplay); + + lsdc_wreg32(ldev, LSDC_CRTC0_HSYNC_REG, + (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN); + + lsdc_wreg32(ldev, LSDC_CRTC0_VSYNC_REG, + (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN); +} + +static void lsdc_crtc1_set_mode(struct lsdc_crtc *lcrtc, + const struct drm_display_mode *mode) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_wreg32(ldev, LSDC_CRTC1_HDISPLAY_REG, + (mode->crtc_htotal << 16) | mode->crtc_hdisplay); + + lsdc_wreg32(ldev, LSDC_CRTC1_VDISPLAY_REG, + (mode->crtc_vtotal << 16) | mode->crtc_vdisplay); + + lsdc_wreg32(ldev, LSDC_CRTC1_HSYNC_REG, + (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN); + + lsdc_wreg32(ldev, LSDC_CRTC1_VSYNC_REG, + (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN); +} + +/* + * This is required for S3 support. + * After resuming from suspend, LSDC_CRTCx_CFG_REG (x = 0 or 1) is filled + * with garbage value, which causes the CRTC hang there. + * + * This function provides minimal settings for the affected registers. + * This overrides the firmware's settings on startup, making the CRTC work + * on our own, similar to the functional of GPU POST (Power On Self Test). + * Only touch CRTC hardware-related parts. + */ + +static void lsdc_crtc0_reset(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, CFG_RESET_N | LSDC_PF_XRGB8888); +} + +static void lsdc_crtc1_reset(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, CFG_RESET_N | LSDC_PF_XRGB8888); +} + +static const struct lsdc_crtc_hw_ops ls7a1000_crtc_hw_ops[2] = { + { + .enable = lsdc_crtc0_enable, + .disable = lsdc_crtc0_disable, + .enable_vblank = lsdc_crtc0_enable_vblank, + .disable_vblank = lsdc_crtc0_disable_vblank, + .flip = lsdc_crtc0_flip, + .clone = lsdc_crtc0_clone, + .set_mode = lsdc_crtc0_set_mode, + .get_scan_pos = lsdc_crtc0_scan_pos, + .soft_reset = lsdc_crtc0_soft_reset, + .reset = lsdc_crtc0_reset, + }, + { + .enable = lsdc_crtc1_enable, + .disable = lsdc_crtc1_disable, + .enable_vblank = lsdc_crtc1_enable_vblank, + .disable_vblank = lsdc_crtc1_disable_vblank, + .flip = lsdc_crtc1_flip, + .clone = lsdc_crtc1_clone, + .set_mode = lsdc_crtc1_set_mode, + .get_scan_pos = lsdc_crtc1_scan_pos, + .soft_reset = lsdc_crtc1_soft_reset, + .reset = lsdc_crtc1_reset, + }, +}; + +/* + * The 32-bit hardware vblank counter has been available since LS7A2000 + * and LS2K2000. The counter increases even though the CRTC is disabled, + * it will be reset only if the CRTC is being soft reset. + * Those registers are also readable for ls7a1000, but its value does not + * change. + */ + +static u32 lsdc_crtc0_get_vblank_count(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + return lsdc_rreg32(ldev, LSDC_CRTC0_VSYNC_COUNTER_REG); +} + +static u32 lsdc_crtc1_get_vblank_count(struct lsdc_crtc *lcrtc) +{ + struct lsdc_device *ldev = lcrtc->ldev; + + return lsdc_rreg32(ldev, LSDC_CRTC1_VSYNC_COUNTER_REG); +} + +/* + * The DMA step bit fields are available since LS7A2000/LS2K2000, for + * supporting odd resolutions. But a large DMA step save the bandwidth. + * The larger, the better. Behavior of writing those bits on LS7A1000 + * or LS2K1000 is underfined. + */ + +static void lsdc_crtc0_set_dma_step(struct lsdc_crtc *lcrtc, + enum lsdc_dma_steps dma_step) +{ + struct lsdc_device *ldev = lcrtc->ldev; + u32 val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); + + val &= ~CFG_DMA_STEP_MASK; + val |= dma_step << CFG_DMA_STEP_SHIFT; + + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val); +} + +static void lsdc_crtc1_set_dma_step(struct lsdc_crtc *lcrtc, + enum lsdc_dma_steps dma_step) +{ + struct lsdc_device *ldev = lcrtc->ldev; + u32 val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); + + val &= ~CFG_DMA_STEP_MASK; + val |= dma_step << CFG_DMA_STEP_SHIFT; + + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val); +} + +static const struct lsdc_crtc_hw_ops ls7a2000_crtc_hw_ops[2] = { + { + .enable = lsdc_crtc0_enable, + .disable = lsdc_crtc0_disable, + .enable_vblank = lsdc_crtc0_enable_vblank, + .disable_vblank = lsdc_crtc0_disable_vblank, + .flip = lsdc_crtc0_flip, + .clone = lsdc_crtc0_clone, + .set_mode = lsdc_crtc0_set_mode, + .soft_reset = lsdc_crtc0_soft_reset, + .get_scan_pos = lsdc_crtc0_scan_pos, + .set_dma_step = lsdc_crtc0_set_dma_step, + .get_vblank_counter = lsdc_crtc0_get_vblank_count, + .reset = lsdc_crtc0_reset, + }, + { + .enable = lsdc_crtc1_enable, + .disable = lsdc_crtc1_disable, + .enable_vblank = lsdc_crtc1_enable_vblank, + .disable_vblank = lsdc_crtc1_disable_vblank, + .flip = lsdc_crtc1_flip, + .clone = lsdc_crtc1_clone, + .set_mode = lsdc_crtc1_set_mode, + .get_scan_pos = lsdc_crtc1_scan_pos, + .soft_reset = lsdc_crtc1_soft_reset, + .set_dma_step = lsdc_crtc1_set_dma_step, + .get_vblank_counter = lsdc_crtc1_get_vblank_count, + .reset = lsdc_crtc1_reset, + }, +}; + +static void lsdc_crtc_reset(struct drm_crtc *crtc) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; + struct lsdc_crtc_state *priv_crtc_state; + + if (crtc->state) + crtc->funcs->atomic_destroy_state(crtc, crtc->state); + + priv_crtc_state = kzalloc(sizeof(*priv_crtc_state), GFP_KERNEL); + + if (!priv_crtc_state) + __drm_atomic_helper_crtc_reset(crtc, NULL); + else + __drm_atomic_helper_crtc_reset(crtc, &priv_crtc_state->base); + + /* Reset the CRTC hardware, this is required for S3 support */ + ops->reset(lcrtc); +} + +static void lsdc_crtc_atomic_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state); + + __drm_atomic_helper_crtc_destroy_state(&priv_state->base); + + kfree(priv_state); +} + +static struct drm_crtc_state * +lsdc_crtc_atomic_duplicate_state(struct drm_crtc *crtc) +{ + struct lsdc_crtc_state *new_priv_state; + struct lsdc_crtc_state *old_priv_state; + + new_priv_state = kzalloc(sizeof(*new_priv_state), GFP_KERNEL); + if (!new_priv_state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &new_priv_state->base); + + old_priv_state = to_lsdc_crtc_state(crtc->state); + + memcpy(&new_priv_state->pparms, &old_priv_state->pparms, + sizeof(new_priv_state->pparms)); + + return &new_priv_state->base; +} + +static u32 lsdc_crtc_get_vblank_counter(struct drm_crtc *crtc) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + + /* 32-bit hardware vblank counter */ + return lcrtc->hw_ops->get_vblank_counter(lcrtc); +} + +static int lsdc_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + + if (!lcrtc->has_vblank) + return -EINVAL; + + lcrtc->hw_ops->enable_vblank(lcrtc); + + return 0; +} + +static void lsdc_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + + if (!lcrtc->has_vblank) + return; + + lcrtc->hw_ops->disable_vblank(lcrtc); +} + +/* + * CRTC related debugfs + * Primary planes and cursor planes belong to the CRTC as well. + * For the sake of convenience, plane-related registers are also add here. + */ + +#define REG_DEF(reg) { \ + .name = __stringify_1(LSDC_##reg##_REG), \ + .offset = LSDC_##reg##_REG, \ +} + +static const struct lsdc_reg32 lsdc_crtc_regs_array[2][21] = { + [0] = { + REG_DEF(CRTC0_CFG), + REG_DEF(CRTC0_FB_ORIGIN), + REG_DEF(CRTC0_DVO_CONF), + REG_DEF(CRTC0_HDISPLAY), + REG_DEF(CRTC0_HSYNC), + REG_DEF(CRTC0_VDISPLAY), + REG_DEF(CRTC0_VSYNC), + REG_DEF(CRTC0_GAMMA_INDEX), + REG_DEF(CRTC0_GAMMA_DATA), + REG_DEF(CRTC0_SYNC_DEVIATION), + REG_DEF(CRTC0_VSYNC_COUNTER), + REG_DEF(CRTC0_SCAN_POS), + REG_DEF(CRTC0_STRIDE), + REG_DEF(CRTC0_FB1_ADDR_HI), + REG_DEF(CRTC0_FB1_ADDR_LO), + REG_DEF(CRTC0_FB0_ADDR_HI), + REG_DEF(CRTC0_FB0_ADDR_LO), + REG_DEF(CURSOR0_CFG), + REG_DEF(CURSOR0_POSITION), + REG_DEF(CURSOR0_BG_COLOR), + REG_DEF(CURSOR0_FG_COLOR), + }, + [1] = { + REG_DEF(CRTC1_CFG), + REG_DEF(CRTC1_FB_ORIGIN), + REG_DEF(CRTC1_DVO_CONF), + REG_DEF(CRTC1_HDISPLAY), + REG_DEF(CRTC1_HSYNC), + REG_DEF(CRTC1_VDISPLAY), + REG_DEF(CRTC1_VSYNC), + REG_DEF(CRTC1_GAMMA_INDEX), + REG_DEF(CRTC1_GAMMA_DATA), + REG_DEF(CRTC1_SYNC_DEVIATION), + REG_DEF(CRTC1_VSYNC_COUNTER), + REG_DEF(CRTC1_SCAN_POS), + REG_DEF(CRTC1_STRIDE), + REG_DEF(CRTC1_FB1_ADDR_HI), + REG_DEF(CRTC1_FB1_ADDR_LO), + REG_DEF(CRTC1_FB0_ADDR_HI), + REG_DEF(CRTC1_FB0_ADDR_LO), + REG_DEF(CURSOR1_CFG), + REG_DEF(CURSOR1_POSITION), + REG_DEF(CURSOR1_BG_COLOR), + REG_DEF(CURSOR1_FG_COLOR), + }, +}; + +static int lsdc_crtc_show_regs(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; + struct lsdc_device *ldev = lcrtc->ldev; + unsigned int i; + + for (i = 0; i < lcrtc->nreg; i++) { + const struct lsdc_reg32 *preg = &lcrtc->preg[i]; + u32 offset = preg->offset; + + seq_printf(m, "%s (0x%04x): 0x%08x\n", + preg->name, offset, lsdc_rreg32(ldev, offset)); + } + + return 0; +} + +static int lsdc_crtc_show_scan_position(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; + int x, y; + + lcrtc->hw_ops->get_scan_pos(lcrtc, &x, &y); + seq_printf(m, "Scanout position: x: %08u, y: %08u\n", x, y); + + return 0; +} + +static int lsdc_crtc_show_vblank_counter(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; + + if (lcrtc->hw_ops->get_vblank_counter) + seq_printf(m, "%s vblank counter: %08u\n\n", lcrtc->base.name, + lcrtc->hw_ops->get_vblank_counter(lcrtc)); + + return 0; +} + +static int lsdc_pixpll_show_clock(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; + struct lsdc_pixpll *pixpll = &lcrtc->pixpll; + const struct lsdc_pixpll_funcs *funcs = pixpll->funcs; + struct drm_crtc *crtc = &lcrtc->base; + struct drm_display_mode *mode = &crtc->state->mode; + struct drm_printer printer = drm_seq_file_printer(m); + unsigned int out_khz; + + out_khz = funcs->get_rate(pixpll); + + seq_printf(m, "%s: %dx%d@%d\n", crtc->name, + mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode)); + + seq_printf(m, "Pixel clock required: %d kHz\n", mode->clock); + seq_printf(m, "Actual frequency output: %u kHz\n", out_khz); + seq_printf(m, "Diff: %d kHz\n", out_khz - mode->clock); + + funcs->print(pixpll, &printer); + + return 0; +} + +static struct drm_info_list lsdc_crtc_debugfs_list[2][4] = { + [0] = { + { "regs", lsdc_crtc_show_regs, 0, NULL }, + { "pixclk", lsdc_pixpll_show_clock, 0, NULL }, + { "scanpos", lsdc_crtc_show_scan_position, 0, NULL }, + { "vblanks", lsdc_crtc_show_vblank_counter, 0, NULL }, + }, + [1] = { + { "regs", lsdc_crtc_show_regs, 0, NULL }, + { "pixclk", lsdc_pixpll_show_clock, 0, NULL }, + { "scanpos", lsdc_crtc_show_scan_position, 0, NULL }, + { "vblanks", lsdc_crtc_show_vblank_counter, 0, NULL }, + }, +}; + +/* operate manually */ + +static int lsdc_crtc_man_op_show(struct seq_file *m, void *data) +{ + seq_puts(m, "soft_reset: soft reset this CRTC\n"); + seq_puts(m, "enable: enable this CRTC\n"); + seq_puts(m, "disable: disable this CRTC\n"); + seq_puts(m, "flip: trigger the page flip\n"); + seq_puts(m, "clone: clone the another crtc with hardware logic\n"); + + return 0; +} + +static int lsdc_crtc_man_op_open(struct inode *inode, struct file *file) +{ + struct drm_crtc *crtc = inode->i_private; + + return single_open(file, lsdc_crtc_man_op_show, crtc); +} + +static ssize_t lsdc_crtc_man_op_write(struct file *file, + const char __user *ubuf, + size_t len, + loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct lsdc_crtc *lcrtc = m->private; + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; + char buf[16]; + + if (len > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, len)) + return -EFAULT; + + buf[len] = '\0'; + + if (sysfs_streq(buf, "soft_reset")) + ops->soft_reset(lcrtc); + else if (sysfs_streq(buf, "enable")) + ops->enable(lcrtc); + else if (sysfs_streq(buf, "disable")) + ops->disable(lcrtc); + else if (sysfs_streq(buf, "flip")) + ops->flip(lcrtc); + else if (sysfs_streq(buf, "clone")) + ops->clone(lcrtc); + + return len; +} + +static const struct file_operations lsdc_crtc_man_op_fops = { + .owner = THIS_MODULE, + .open = lsdc_crtc_man_op_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = lsdc_crtc_man_op_write, +}; + +static int lsdc_crtc_late_register(struct drm_crtc *crtc) +{ + struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc); + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + struct drm_minor *minor = crtc->dev->primary; + unsigned int index = dispipe->index; + unsigned int i; + + lcrtc->preg = lsdc_crtc_regs_array[index]; + lcrtc->nreg = ARRAY_SIZE(lsdc_crtc_regs_array[index]); + lcrtc->p_info_list = lsdc_crtc_debugfs_list[index]; + lcrtc->n_info_list = ARRAY_SIZE(lsdc_crtc_debugfs_list[index]); + + for (i = 0; i < lcrtc->n_info_list; ++i) + lcrtc->p_info_list[i].data = lcrtc; + + drm_debugfs_create_files(lcrtc->p_info_list, lcrtc->n_info_list, + crtc->debugfs_entry, minor); + + /* Manual operations supported */ + debugfs_create_file("ops", 0644, crtc->debugfs_entry, lcrtc, + &lsdc_crtc_man_op_fops); + + return 0; +} + +static void lsdc_crtc_atomic_print_state(struct drm_printer *p, + const struct drm_crtc_state *state) +{ + const struct lsdc_crtc_state *priv_state; + const struct lsdc_pixpll_parms *pparms; + + priv_state = container_of_const(state, struct lsdc_crtc_state, base); + pparms = &priv_state->pparms; + + drm_printf(p, "\tInput clock divider = %u\n", pparms->div_ref); + drm_printf(p, "\tMedium clock multiplier = %u\n", pparms->loopc); + drm_printf(p, "\tOutput clock divider = %u\n", pparms->div_out); +} + +static const struct drm_crtc_funcs ls7a1000_crtc_funcs = { + .reset = lsdc_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state, + .atomic_destroy_state = lsdc_crtc_atomic_destroy_state, + .late_register = lsdc_crtc_late_register, + .enable_vblank = lsdc_crtc_enable_vblank, + .disable_vblank = lsdc_crtc_disable_vblank, + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, + .atomic_print_state = lsdc_crtc_atomic_print_state, +}; + +static const struct drm_crtc_funcs ls7a2000_crtc_funcs = { + .reset = lsdc_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state, + .atomic_destroy_state = lsdc_crtc_atomic_destroy_state, + .late_register = lsdc_crtc_late_register, + .get_vblank_counter = lsdc_crtc_get_vblank_counter, + .enable_vblank = lsdc_crtc_enable_vblank, + .disable_vblank = lsdc_crtc_disable_vblank, + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, + .atomic_print_state = lsdc_crtc_atomic_print_state, +}; + +static enum drm_mode_status +lsdc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) +{ + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_desc *descp = ldev->descp; + unsigned int pitch; + + if (mode->hdisplay > descp->max_width) + return MODE_BAD_HVALUE; + + if (mode->vdisplay > descp->max_height) + return MODE_BAD_VVALUE; + + if (mode->clock > descp->max_pixel_clk) { + drm_dbg_kms(ddev, "mode %dx%d, pixel clock=%d is too high\n", + mode->hdisplay, mode->vdisplay, mode->clock); + return MODE_CLOCK_HIGH; + } + + /* 4 for DRM_FORMAT_XRGB8888 */ + pitch = mode->hdisplay * 4; + + if (pitch % descp->pitch_align) { + drm_dbg_kms(ddev, "align to %u bytes is required: %u\n", + descp->pitch_align, pitch); + return MODE_BAD_WIDTH; + } + + return MODE_OK; +} + +static int lsdc_pixpll_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + struct lsdc_pixpll *pixpll = &lcrtc->pixpll; + const struct lsdc_pixpll_funcs *pfuncs = pixpll->funcs; + struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state); + unsigned int clock = state->mode.clock; + int ret; + + ret = pfuncs->compute(pixpll, clock, &priv_state->pparms); + if (ret) { + drm_warn(crtc->dev, "Failed to find PLL params for %ukHz\n", + clock); + return -EINVAL; + } + + return 0; +} + +static int lsdc_crtc_helper_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + if (!crtc_state->enable) + return 0; + + return lsdc_pixpll_atomic_check(crtc, crtc_state); +} + +static void lsdc_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + const struct lsdc_crtc_hw_ops *crtc_hw_ops = lcrtc->hw_ops; + struct lsdc_pixpll *pixpll = &lcrtc->pixpll; + const struct lsdc_pixpll_funcs *pixpll_funcs = pixpll->funcs; + struct drm_crtc_state *state = crtc->state; + struct drm_display_mode *mode = &state->mode; + struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state); + + pixpll_funcs->update(pixpll, &priv_state->pparms); + + if (crtc_hw_ops->set_dma_step) { + unsigned int width_in_bytes = mode->hdisplay * 4; + enum lsdc_dma_steps dma_step; + + /* + * Using DMA step as large as possible, for improving + * hardware DMA efficiency. + */ + if (width_in_bytes % 256 == 0) + dma_step = LSDC_DMA_STEP_256_BYTES; + else if (width_in_bytes % 128 == 0) + dma_step = LSDC_DMA_STEP_128_BYTES; + else if (width_in_bytes % 64 == 0) + dma_step = LSDC_DMA_STEP_64_BYTES; + else /* width_in_bytes % 32 == 0 */ + dma_step = LSDC_DMA_STEP_32_BYTES; + + crtc_hw_ops->set_dma_step(lcrtc, dma_step); + } + + crtc_hw_ops->set_mode(lcrtc, mode); +} + +static void lsdc_crtc_send_vblank(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + unsigned long flags; + + if (!crtc->state || !crtc->state->event) + return; + + drm_dbg(ddev, "Send vblank manually\n"); + + spin_lock_irqsave(&ddev->event_lock, flags); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + spin_unlock_irqrestore(&ddev->event_lock, flags); +} + +static void lsdc_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + + if (lcrtc->has_vblank) + drm_crtc_vblank_on(crtc); + + lcrtc->hw_ops->enable(lcrtc); +} + +static void lsdc_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + + if (lcrtc->has_vblank) + drm_crtc_vblank_off(crtc); + + lcrtc->hw_ops->disable(lcrtc); + + /* + * Make sure we issue a vblank event after disabling the CRTC if + * someone was waiting it. + */ + lsdc_crtc_send_vblank(crtc); +} + +static void lsdc_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, crtc->state->event); + else + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static bool lsdc_crtc_get_scanout_position(struct drm_crtc *crtc, + bool in_vblank_irq, + int *vpos, + int *hpos, + ktime_t *stime, + ktime_t *etime, + const struct drm_display_mode *mode) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; + int vsw, vbp, vactive_start, vactive_end, vfp_end; + int x, y; + + vsw = mode->crtc_vsync_end - mode->crtc_vsync_start; + vbp = mode->crtc_vtotal - mode->crtc_vsync_end; + + vactive_start = vsw + vbp + 1; + vactive_end = vactive_start + mode->crtc_vdisplay; + + /* last scan line before VSYNC */ + vfp_end = mode->crtc_vtotal; + + if (stime) + *stime = ktime_get(); + + ops->get_scan_pos(lcrtc, &x, &y); + + if (y > vactive_end) + y = y - vfp_end - vactive_start; + else + y -= vactive_start; + + *vpos = y; + *hpos = 0; + + if (etime) + *etime = ktime_get(); + + return true; +} + +static const struct drm_crtc_helper_funcs lsdc_crtc_helper_funcs = { + .mode_valid = lsdc_crtc_mode_valid, + .mode_set_nofb = lsdc_crtc_mode_set_nofb, + .atomic_enable = lsdc_crtc_atomic_enable, + .atomic_disable = lsdc_crtc_atomic_disable, + .atomic_check = lsdc_crtc_helper_atomic_check, + .atomic_flush = lsdc_crtc_atomic_flush, + .get_scanout_position = lsdc_crtc_get_scanout_position, +}; + +int ls7a1000_crtc_init(struct drm_device *ddev, + struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + unsigned int index, + bool has_vblank) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + int ret; + + ret = lsdc_pixpll_init(&lcrtc->pixpll, ddev, index); + if (ret) { + drm_err(ddev, "pixel pll init failed: %d\n", ret); + return ret; + } + + lcrtc->ldev = to_lsdc(ddev); + lcrtc->has_vblank = has_vblank; + lcrtc->hw_ops = &ls7a1000_crtc_hw_ops[index]; + + ret = drm_crtc_init_with_planes(ddev, crtc, primary, cursor, + &ls7a1000_crtc_funcs, + "LS-CRTC-%d", index); + if (ret) { + drm_err(ddev, "crtc init with planes failed: %d\n", ret); + return ret; + } + + drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs); + + ret = drm_mode_crtc_set_gamma_size(crtc, 256); + if (ret) + return ret; + + drm_crtc_enable_color_mgmt(crtc, 0, false, 256); + + return 0; +} + +int ls7a2000_crtc_init(struct drm_device *ddev, + struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + unsigned int index, + bool has_vblank) +{ + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); + int ret; + + ret = lsdc_pixpll_init(&lcrtc->pixpll, ddev, index); + if (ret) { + drm_err(ddev, "crtc init with pll failed: %d\n", ret); + return ret; + } + + lcrtc->ldev = to_lsdc(ddev); + lcrtc->has_vblank = has_vblank; + lcrtc->hw_ops = &ls7a2000_crtc_hw_ops[index]; + + ret = drm_crtc_init_with_planes(ddev, crtc, primary, cursor, + &ls7a2000_crtc_funcs, + "LS-CRTC-%u", index); + if (ret) { + drm_err(ddev, "crtc init with planes failed: %d\n", ret); + return ret; + } + + drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs); + + ret = drm_mode_crtc_set_gamma_size(crtc, 256); + if (ret) + return ret; + + drm_crtc_enable_color_mgmt(crtc, 0, false, 256); + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_debugfs.c b/drivers/gpu/drm/loongson/lsdc_debugfs.c new file mode 100644 index 000000000000..b9c2e6b1701f --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_debugfs.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <drm/drm_debugfs.h> + +#include "lsdc_benchmark.h" +#include "lsdc_drv.h" +#include "lsdc_gem.h" +#include "lsdc_probe.h" +#include "lsdc_ttm.h" + +/* device level debugfs */ + +static int lsdc_identify(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data; + const struct loongson_gfx_desc *gfx = to_loongson_gfx(ldev->descp); + u8 impl, rev; + + loongson_cpu_get_prid(&impl, &rev); + + seq_printf(m, "Running on cpu 0x%x, cpu revision: 0x%x\n", + impl, rev); + + seq_printf(m, "Contained in: %s\n", gfx->model); + + return 0; +} + +static int lsdc_show_mm(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct drm_printer p = drm_seq_file_printer(m); + + drm_mm_print(&ddev->vma_offset_manager->vm_addr_space_mm, &p); + + return 0; +} + +static int lsdc_show_gfxpll_clock(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data; + struct drm_printer printer = drm_seq_file_printer(m); + struct loongson_gfxpll *gfxpll = ldev->gfxpll; + + gfxpll->funcs->print(gfxpll, &printer, true); + + return 0; +} + +static int lsdc_show_benchmark(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data; + struct drm_printer printer = drm_seq_file_printer(m); + + lsdc_show_benchmark_copy(ldev, &printer); + + return 0; +} + +static int lsdc_pdev_enable_io_mem(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data; + u16 cmd; + + pci_read_config_word(ldev->dc, PCI_COMMAND, &cmd); + + seq_printf(m, "PCI_COMMAND: 0x%x\n", cmd); + + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_IO; + + pci_write_config_word(ldev->dc, PCI_COMMAND, cmd); + + pci_read_config_word(ldev->dc, PCI_COMMAND, &cmd); + + seq_printf(m, "PCI_COMMAND: 0x%x\n", cmd); + + return 0; +} + +static struct drm_info_list lsdc_debugfs_list[] = { + { "benchmark", lsdc_show_benchmark, 0, NULL }, + { "bos", lsdc_show_buffer_object, 0, NULL }, + { "chips", lsdc_identify, 0, NULL }, + { "clocks", lsdc_show_gfxpll_clock, 0, NULL }, + { "dc_enable", lsdc_pdev_enable_io_mem, 0, NULL }, + { "mm", lsdc_show_mm, 0, NULL }, +}; + +void lsdc_debugfs_init(struct drm_minor *minor) +{ + struct drm_device *ddev = minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + unsigned int n = ARRAY_SIZE(lsdc_debugfs_list); + unsigned int i; + + for (i = 0; i < n; ++i) + lsdc_debugfs_list[i].data = ldev; + + drm_debugfs_create_files(lsdc_debugfs_list, n, minor->debugfs_root, minor); + + lsdc_ttm_debugfs_init(ldev); +} diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c new file mode 100644 index 000000000000..188ec82afcfb --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_drv.c @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/pci.h> +#include <linux/vgaarb.h> + +#include <drm/drm_aperture.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_generic.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_modeset_helper.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> + +#include "loongson_module.h" +#include "lsdc_drv.h" +#include "lsdc_gem.h" +#include "lsdc_ttm.h" + +#define DRIVER_AUTHOR "Sui Jingfeng <suijingfeng@loongson.cn>" +#define DRIVER_NAME "loongson" +#define DRIVER_DESC "drm driver for loongson graphics" +#define DRIVER_DATE "20220701" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +DEFINE_DRM_GEM_FOPS(lsdc_gem_fops); + +static const struct drm_driver lsdc_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_RENDER | DRIVER_GEM | DRIVER_ATOMIC, + .fops = &lsdc_gem_fops, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + + .debugfs_init = lsdc_debugfs_init, + .dumb_create = lsdc_dumb_create, + .dumb_map_offset = lsdc_dumb_map_offset, + .gem_prime_import_sg_table = lsdc_prime_import_sg_table, +}; + +static const struct drm_mode_config_funcs lsdc_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +/* Display related */ + +static int lsdc_modeset_init(struct lsdc_device *ldev, + unsigned int num_crtc, + const struct lsdc_kms_funcs *funcs, + bool has_vblank) +{ + struct drm_device *ddev = &ldev->base; + struct lsdc_display_pipe *dispipe; + unsigned int i; + int ret; + + for (i = 0; i < num_crtc; i++) { + dispipe = &ldev->dispipe[i]; + + /* We need an index before crtc is initialized */ + dispipe->index = i; + + ret = funcs->create_i2c(ddev, dispipe, i); + if (ret) + return ret; + } + + for (i = 0; i < num_crtc; i++) { + struct i2c_adapter *ddc = NULL; + + dispipe = &ldev->dispipe[i]; + if (dispipe->li2c) + ddc = &dispipe->li2c->adapter; + + ret = funcs->output_init(ddev, dispipe, ddc, i); + if (ret) + return ret; + + ldev->num_output++; + } + + for (i = 0; i < num_crtc; i++) { + dispipe = &ldev->dispipe[i]; + + ret = funcs->primary_plane_init(ddev, &dispipe->primary.base, i); + if (ret) + return ret; + + ret = funcs->cursor_plane_init(ddev, &dispipe->cursor.base, i); + if (ret) + return ret; + + ret = funcs->crtc_init(ddev, &dispipe->crtc.base, + &dispipe->primary.base, + &dispipe->cursor.base, + i, has_vblank); + if (ret) + return ret; + } + + drm_info(ddev, "Total %u outputs\n", ldev->num_output); + + return 0; +} + +static const struct drm_mode_config_helper_funcs lsdc_mode_config_helper_funcs = { + .atomic_commit_tail = drm_atomic_helper_commit_tail, +}; + +static int lsdc_mode_config_init(struct drm_device *ddev, + const struct lsdc_desc *descp) +{ + int ret; + + ret = drmm_mode_config_init(ddev); + if (ret) + return ret; + + ddev->mode_config.funcs = &lsdc_mode_config_funcs; + ddev->mode_config.min_width = 1; + ddev->mode_config.min_height = 1; + ddev->mode_config.max_width = descp->max_width * LSDC_NUM_CRTC; + ddev->mode_config.max_height = descp->max_height * LSDC_NUM_CRTC; + ddev->mode_config.preferred_depth = 24; + ddev->mode_config.prefer_shadow = 1; + + ddev->mode_config.cursor_width = descp->hw_cursor_h; + ddev->mode_config.cursor_height = descp->hw_cursor_h; + + ddev->mode_config.helper_private = &lsdc_mode_config_helper_funcs; + + if (descp->has_vblank_counter) + ddev->max_vblank_count = 0xffffffff; + + return ret; +} + +/* + * The GPU and display controller in the LS7A1000/LS7A2000/LS2K2000 are + * separated PCIE devices. They are two devices, not one. Bar 2 of the GPU + * device contains the base address and size of the VRAM, both the GPU and + * the DC could access the on-board VRAM. + */ +static int lsdc_get_dedicated_vram(struct lsdc_device *ldev, + struct pci_dev *pdev_dc, + const struct lsdc_desc *descp) +{ + struct drm_device *ddev = &ldev->base; + struct pci_dev *pdev_gpu; + resource_size_t base, size; + + /* + * The GPU has 00:06.0 as its BDF, while the DC has 00:06.1 + * This is true for the LS7A1000, LS7A2000 and LS2K2000. + */ + pdev_gpu = pci_get_domain_bus_and_slot(pci_domain_nr(pdev_dc->bus), + pdev_dc->bus->number, + PCI_DEVFN(6, 0)); + if (!pdev_gpu) { + drm_err(ddev, "No GPU device, then no VRAM\n"); + return -ENODEV; + } + + base = pci_resource_start(pdev_gpu, 2); + size = pci_resource_len(pdev_gpu, 2); + + ldev->vram_base = base; + ldev->vram_size = size; + ldev->gpu = pdev_gpu; + + drm_info(ddev, "Dedicated vram start: 0x%llx, size: %uMiB\n", + (u64)base, (u32)(size >> 20)); + + return 0; +} + +static struct lsdc_device * +lsdc_create_device(struct pci_dev *pdev, + const struct lsdc_desc *descp, + const struct drm_driver *driver) +{ + struct lsdc_device *ldev; + struct drm_device *ddev; + int ret; + + ldev = devm_drm_dev_alloc(&pdev->dev, driver, struct lsdc_device, base); + if (IS_ERR(ldev)) + return ldev; + + ldev->dc = pdev; + ldev->descp = descp; + + ddev = &ldev->base; + + loongson_gfxpll_create(ddev, &ldev->gfxpll); + + ret = lsdc_get_dedicated_vram(ldev, pdev, descp); + if (ret) { + drm_err(ddev, "Init VRAM failed: %d\n", ret); + return ERR_PTR(ret); + } + + ret = drm_aperture_remove_conflicting_framebuffers(ldev->vram_base, + ldev->vram_size, + driver); + if (ret) { + drm_err(ddev, "Remove firmware framebuffers failed: %d\n", ret); + return ERR_PTR(ret); + } + + ret = lsdc_ttm_init(ldev); + if (ret) { + drm_err(ddev, "Memory manager init failed: %d\n", ret); + return ERR_PTR(ret); + } + + lsdc_gem_init(ddev); + + /* Bar 0 of the DC device contains the MMIO register's base address */ + ldev->reg_base = pcim_iomap(pdev, 0, 0); + if (!ldev->reg_base) + return ERR_PTR(-ENODEV); + + spin_lock_init(&ldev->reglock); + + ret = lsdc_mode_config_init(ddev, descp); + if (ret) + return ERR_PTR(ret); + + ret = lsdc_modeset_init(ldev, descp->num_of_crtc, descp->funcs, + loongson_vblank); + if (ret) + return ERR_PTR(ret); + + drm_mode_config_reset(ddev); + + return ldev; +} + +/* For multiple GPU driver instance co-exixt in the system */ + +static unsigned int lsdc_vga_set_decode(struct pci_dev *pdev, bool state) +{ + return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; +} + +static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + const struct lsdc_desc *descp; + struct drm_device *ddev; + struct lsdc_device *ldev; + int ret; + + descp = lsdc_device_probe(pdev, ent->driver_data); + if (IS_ERR_OR_NULL(descp)) + return -ENODEV; + + pci_set_master(pdev); + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); + if (ret) + return ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + dev_info(&pdev->dev, "Found %s, revision: %u", + to_loongson_gfx(descp)->model, pdev->revision); + + ldev = lsdc_create_device(pdev, descp, &lsdc_drm_driver); + if (IS_ERR(ldev)) + return PTR_ERR(ldev); + + ddev = &ldev->base; + + pci_set_drvdata(pdev, ddev); + + vga_client_register(pdev, lsdc_vga_set_decode); + + drm_kms_helper_poll_init(ddev); + + if (loongson_vblank) { + ret = drm_vblank_init(ddev, descp->num_of_crtc); + if (ret) + return ret; + + ret = devm_request_irq(&pdev->dev, pdev->irq, + descp->funcs->irq_handler, + IRQF_SHARED, + dev_name(&pdev->dev), ddev); + if (ret) { + drm_err(ddev, "Failed to register interrupt: %d\n", ret); + return ret; + } + + drm_info(ddev, "registered irq: %u\n", pdev->irq); + } + + ret = drm_dev_register(ddev, 0); + if (ret) + return ret; + + drm_fbdev_generic_setup(ddev, 32); + + return 0; +} + +static void lsdc_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *ddev = pci_get_drvdata(pdev); + + drm_dev_unregister(ddev); + drm_atomic_helper_shutdown(ddev); +} + +static int lsdc_drm_freeze(struct drm_device *ddev) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + struct lsdc_bo *lbo; + int ret; + + /* unpin all of buffers in the VRAM */ + mutex_lock(&ldev->gem.mutex); + list_for_each_entry(lbo, &ldev->gem.objects, list) { + struct ttm_buffer_object *tbo = &lbo->tbo; + struct ttm_resource *resource = tbo->resource; + unsigned int pin_count = tbo->pin_count; + + drm_dbg(ddev, "bo[%p], size: %zuKiB, type: %s, pin count: %u\n", + lbo, lsdc_bo_size(lbo) >> 10, + lsdc_mem_type_to_str(resource->mem_type), pin_count); + + if (!pin_count) + continue; + + if (resource->mem_type == TTM_PL_VRAM) { + ret = lsdc_bo_reserve(lbo); + if (unlikely(ret)) { + drm_err(ddev, "bo reserve failed: %d\n", ret); + continue; + } + + do { + lsdc_bo_unpin(lbo); + --pin_count; + } while (pin_count); + + lsdc_bo_unreserve(lbo); + } + } + mutex_unlock(&ldev->gem.mutex); + + lsdc_bo_evict_vram(ddev); + + ret = drm_mode_config_helper_suspend(ddev); + if (unlikely(ret)) { + drm_err(ddev, "Freeze error: %d", ret); + return ret; + } + + return 0; +} + +static int lsdc_drm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *ddev = pci_get_drvdata(pdev); + + return drm_mode_config_helper_resume(ddev); +} + +static int lsdc_pm_freeze(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *ddev = pci_get_drvdata(pdev); + + return lsdc_drm_freeze(ddev); +} + +static int lsdc_pm_thaw(struct device *dev) +{ + return lsdc_drm_resume(dev); +} + +static int lsdc_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int error; + + error = lsdc_pm_freeze(dev); + if (error) + return error; + + pci_save_state(pdev); + /* Shut down the device */ + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int lsdc_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + pci_set_power_state(pdev, PCI_D0); + + pci_restore_state(pdev); + + if (pcim_enable_device(pdev)) + return -EIO; + + return lsdc_pm_thaw(dev); +} + +static const struct dev_pm_ops lsdc_pm_ops = { + .suspend = lsdc_pm_suspend, + .resume = lsdc_pm_resume, + .freeze = lsdc_pm_freeze, + .thaw = lsdc_pm_thaw, + .poweroff = lsdc_pm_freeze, + .restore = lsdc_pm_resume, +}; + +static const struct pci_device_id lsdc_pciid_list[] = { + {PCI_VDEVICE(LOONGSON, 0x7a06), CHIP_LS7A1000}, + {PCI_VDEVICE(LOONGSON, 0x7a36), CHIP_LS7A2000}, + { } +}; + +struct pci_driver lsdc_pci_driver = { + .name = DRIVER_NAME, + .id_table = lsdc_pciid_list, + .probe = lsdc_pci_probe, + .remove = lsdc_pci_remove, + .driver.pm = &lsdc_pm_ops, +}; + +MODULE_DEVICE_TABLE(pci, lsdc_pciid_list); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/loongson/lsdc_drv.h b/drivers/gpu/drm/loongson/lsdc_drv.h new file mode 100644 index 000000000000..fbf2d760ef27 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_drv.h @@ -0,0 +1,388 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_DRV_H__ +#define __LSDC_DRV_H__ + +#include <linux/pci.h> + +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_encoder.h> +#include <drm/drm_file.h> +#include <drm/drm_plane.h> +#include <drm/ttm/ttm_device.h> + +#include "lsdc_i2c.h" +#include "lsdc_irq.h" +#include "lsdc_gfxpll.h" +#include "lsdc_output.h" +#include "lsdc_pixpll.h" +#include "lsdc_regs.h" + +/* Currently, all Loongson display controllers have two display pipes. */ +#define LSDC_NUM_CRTC 2 + +/* + * LS7A1000/LS7A2000 chipsets function as the south & north bridges of the + * Loongson 3 series processors, they are equipped with on-board video RAM + * typically. While Loongson LS2K series are low cost SoCs which share the + * system RAM as video RAM, they don't has a dedicated VRAM. + * + * There is only a 1:1 mapping of crtcs, encoders and connectors for the DC + * + * display pipe 0 = crtc0 + dvo0 + encoder0 + connector0 + cursor0 + primary0 + * display pipe 1 = crtc1 + dvo1 + encoder1 + connectro1 + cursor1 + primary1 + */ + +enum loongson_chip_id { + CHIP_LS7A1000 = 0, + CHIP_LS7A2000 = 1, + CHIP_LS_LAST, +}; + +const struct lsdc_desc * +lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip); + +struct lsdc_kms_funcs; + +/* DC specific */ + +struct lsdc_desc { + u32 num_of_crtc; + u32 max_pixel_clk; + u32 max_width; + u32 max_height; + u32 num_of_hw_cursor; + u32 hw_cursor_w; + u32 hw_cursor_h; + u32 pitch_align; /* CRTC DMA alignment constraint */ + bool has_vblank_counter; /* 32 bit hw vsync counter */ + + /* device dependent ops, dc side */ + const struct lsdc_kms_funcs *funcs; +}; + +/* GFX related resources wrangler */ + +struct loongson_gfx_desc { + struct lsdc_desc dc; + + u32 conf_reg_base; + + /* GFXPLL shared by the DC, GMC and GPU */ + struct { + u32 reg_offset; + u32 reg_size; + } gfxpll; + + /* Pixel PLL, per display pipe */ + struct { + u32 reg_offset; + u32 reg_size; + } pixpll[LSDC_NUM_CRTC]; + + enum loongson_chip_id chip_id; + char model[64]; +}; + +static inline const struct loongson_gfx_desc * +to_loongson_gfx(const struct lsdc_desc *dcp) +{ + return container_of_const(dcp, struct loongson_gfx_desc, dc); +}; + +struct lsdc_reg32 { + char *name; + u32 offset; +}; + +/* crtc hardware related ops */ + +struct lsdc_crtc; + +struct lsdc_crtc_hw_ops { + void (*enable)(struct lsdc_crtc *lcrtc); + void (*disable)(struct lsdc_crtc *lcrtc); + void (*enable_vblank)(struct lsdc_crtc *lcrtc); + void (*disable_vblank)(struct lsdc_crtc *lcrtc); + void (*flip)(struct lsdc_crtc *lcrtc); + void (*clone)(struct lsdc_crtc *lcrtc); + void (*get_scan_pos)(struct lsdc_crtc *lcrtc, int *hpos, int *vpos); + void (*set_mode)(struct lsdc_crtc *lcrtc, const struct drm_display_mode *mode); + void (*soft_reset)(struct lsdc_crtc *lcrtc); + void (*reset)(struct lsdc_crtc *lcrtc); + + u32 (*get_vblank_counter)(struct lsdc_crtc *lcrtc); + void (*set_dma_step)(struct lsdc_crtc *lcrtc, enum lsdc_dma_steps step); +}; + +struct lsdc_crtc { + struct drm_crtc base; + struct lsdc_pixpll pixpll; + struct lsdc_device *ldev; + const struct lsdc_crtc_hw_ops *hw_ops; + const struct lsdc_reg32 *preg; + unsigned int nreg; + struct drm_info_list *p_info_list; + unsigned int n_info_list; + bool has_vblank; +}; + +/* primary plane hardware related ops */ + +struct lsdc_primary; + +struct lsdc_primary_plane_ops { + void (*update_fb_addr)(struct lsdc_primary *plane, u64 addr); + void (*update_fb_stride)(struct lsdc_primary *plane, u32 stride); + void (*update_fb_format)(struct lsdc_primary *plane, + const struct drm_format_info *format); +}; + +struct lsdc_primary { + struct drm_plane base; + const struct lsdc_primary_plane_ops *ops; + struct lsdc_device *ldev; +}; + +/* cursor plane hardware related ops */ + +struct lsdc_cursor; + +struct lsdc_cursor_plane_ops { + void (*update_bo_addr)(struct lsdc_cursor *plane, u64 addr); + void (*update_cfg)(struct lsdc_cursor *plane, + enum lsdc_cursor_size cursor_size, + enum lsdc_cursor_format); + void (*update_position)(struct lsdc_cursor *plane, int x, int y); +}; + +struct lsdc_cursor { + struct drm_plane base; + const struct lsdc_cursor_plane_ops *ops; + struct lsdc_device *ldev; +}; + +struct lsdc_output { + struct drm_encoder encoder; + struct drm_connector connector; +}; + +static inline struct lsdc_output * +connector_to_lsdc_output(struct drm_connector *connector) +{ + return container_of(connector, struct lsdc_output, connector); +} + +static inline struct lsdc_output * +encoder_to_lsdc_output(struct drm_encoder *encoder) +{ + return container_of(encoder, struct lsdc_output, encoder); +} + +struct lsdc_display_pipe { + struct lsdc_crtc crtc; + struct lsdc_primary primary; + struct lsdc_cursor cursor; + struct lsdc_output output; + struct lsdc_i2c *li2c; + unsigned int index; +}; + +static inline struct lsdc_display_pipe * +output_to_display_pipe(struct lsdc_output *output) +{ + return container_of(output, struct lsdc_display_pipe, output); +} + +struct lsdc_kms_funcs { + irqreturn_t (*irq_handler)(int irq, void *arg); + + int (*create_i2c)(struct drm_device *ddev, + struct lsdc_display_pipe *dispipe, + unsigned int index); + + int (*output_init)(struct drm_device *ddev, + struct lsdc_display_pipe *dispipe, + struct i2c_adapter *ddc, + unsigned int index); + + int (*cursor_plane_init)(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index); + + int (*primary_plane_init)(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index); + + int (*crtc_init)(struct drm_device *ddev, + struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + unsigned int index, + bool has_vblank); +}; + +static inline struct lsdc_crtc * +to_lsdc_crtc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct lsdc_crtc, base); +} + +static inline struct lsdc_display_pipe * +crtc_to_display_pipe(struct drm_crtc *crtc) +{ + return container_of(crtc, struct lsdc_display_pipe, crtc.base); +} + +static inline struct lsdc_primary * +to_lsdc_primary(struct drm_plane *plane) +{ + return container_of(plane, struct lsdc_primary, base); +} + +static inline struct lsdc_cursor * +to_lsdc_cursor(struct drm_plane *plane) +{ + return container_of(plane, struct lsdc_cursor, base); +} + +struct lsdc_crtc_state { + struct drm_crtc_state base; + struct lsdc_pixpll_parms pparms; +}; + +struct lsdc_gem { + /* @mutex: protect objects list */ + struct mutex mutex; + struct list_head objects; +}; + +struct lsdc_device { + struct drm_device base; + struct ttm_device bdev; + + /* @descp: features description of the DC variant */ + const struct lsdc_desc *descp; + struct pci_dev *dc; + struct pci_dev *gpu; + + struct loongson_gfxpll *gfxpll; + + /* @reglock: protects concurrent access */ + spinlock_t reglock; + + void __iomem *reg_base; + resource_size_t vram_base; + resource_size_t vram_size; + + resource_size_t gtt_base; + resource_size_t gtt_size; + + struct lsdc_display_pipe dispipe[LSDC_NUM_CRTC]; + + struct lsdc_gem gem; + + u32 irq_status; + + /* tracking pinned memory */ + size_t vram_pinned_size; + size_t gtt_pinned_size; + + /* @num_output: count the number of active display pipe */ + unsigned int num_output; +}; + +static inline struct lsdc_device *tdev_to_ldev(struct ttm_device *bdev) +{ + return container_of(bdev, struct lsdc_device, bdev); +} + +static inline struct lsdc_device *to_lsdc(struct drm_device *ddev) +{ + return container_of(ddev, struct lsdc_device, base); +} + +static inline struct lsdc_crtc_state * +to_lsdc_crtc_state(struct drm_crtc_state *base) +{ + return container_of(base, struct lsdc_crtc_state, base); +} + +void lsdc_debugfs_init(struct drm_minor *minor); + +int ls7a1000_crtc_init(struct drm_device *ddev, + struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + unsigned int index, + bool no_vblank); + +int ls7a2000_crtc_init(struct drm_device *ddev, + struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + unsigned int index, + bool no_vblank); + +int lsdc_primary_plane_init(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index); + +int ls7a1000_cursor_plane_init(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index); + +int ls7a2000_cursor_plane_init(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index); + +/* Registers access helpers */ + +static inline u32 lsdc_rreg32(struct lsdc_device *ldev, u32 offset) +{ + return readl(ldev->reg_base + offset); +} + +static inline void lsdc_wreg32(struct lsdc_device *ldev, u32 offset, u32 val) +{ + writel(val, ldev->reg_base + offset); +} + +static inline void lsdc_ureg32_set(struct lsdc_device *ldev, + u32 offset, + u32 mask) +{ + void __iomem *addr = ldev->reg_base + offset; + u32 val = readl(addr); + + writel(val | mask, addr); +} + +static inline void lsdc_ureg32_clr(struct lsdc_device *ldev, + u32 offset, + u32 mask) +{ + void __iomem *addr = ldev->reg_base + offset; + u32 val = readl(addr); + + writel(val & ~mask, addr); +} + +static inline u32 lsdc_pipe_rreg32(struct lsdc_device *ldev, + u32 offset, u32 pipe) +{ + return readl(ldev->reg_base + offset + pipe * CRTC_PIPE_OFFSET); +} + +static inline void lsdc_pipe_wreg32(struct lsdc_device *ldev, + u32 offset, u32 pipe, u32 val) +{ + writel(val, ldev->reg_base + offset + pipe * CRTC_PIPE_OFFSET); +} + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_gem.c b/drivers/gpu/drm/loongson/lsdc_gem.c new file mode 100644 index 000000000000..04293df2f0de --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_gem.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/dma-buf.h> + +#include <drm/drm_debugfs.h> +#include <drm/drm_file.h> +#include <drm/drm_gem.h> +#include <drm/drm_prime.h> + +#include "lsdc_drv.h" +#include "lsdc_gem.h" +#include "lsdc_ttm.h" + +static int lsdc_gem_prime_pin(struct drm_gem_object *obj) +{ + struct lsdc_bo *lbo = gem_to_lsdc_bo(obj); + int ret; + + ret = lsdc_bo_reserve(lbo); + if (unlikely(ret)) + return ret; + + ret = lsdc_bo_pin(lbo, LSDC_GEM_DOMAIN_GTT, NULL); + if (likely(ret == 0)) + lbo->sharing_count++; + + lsdc_bo_unreserve(lbo); + + return ret; +} + +static void lsdc_gem_prime_unpin(struct drm_gem_object *obj) +{ + struct lsdc_bo *lbo = gem_to_lsdc_bo(obj); + int ret; + + ret = lsdc_bo_reserve(lbo); + if (unlikely(ret)) + return; + + lsdc_bo_unpin(lbo); + if (lbo->sharing_count) + lbo->sharing_count--; + + lsdc_bo_unreserve(lbo); +} + +static struct sg_table *lsdc_gem_prime_get_sg_table(struct drm_gem_object *obj) +{ + struct ttm_buffer_object *tbo = to_ttm_bo(obj); + struct ttm_tt *tt = tbo->ttm; + + if (!tt) { + drm_err(obj->dev, "sharing a buffer without backing memory\n"); + return ERR_PTR(-ENOMEM); + } + + return drm_prime_pages_to_sg(obj->dev, tt->pages, tt->num_pages); +} + +static void lsdc_gem_object_free(struct drm_gem_object *obj) +{ + struct ttm_buffer_object *tbo = to_ttm_bo(obj); + + if (tbo) + ttm_bo_put(tbo); +} + +static int lsdc_gem_object_vmap(struct drm_gem_object *obj, struct iosys_map *map) +{ + struct ttm_buffer_object *tbo = to_ttm_bo(obj); + struct lsdc_bo *lbo = to_lsdc_bo(tbo); + int ret; + + if (lbo->vmap_count > 0) { + ++lbo->vmap_count; + goto out; + } + + ret = lsdc_bo_pin(lbo, 0, NULL); + if (unlikely(ret)) { + drm_err(obj->dev, "pin %p for vmap failed\n", lbo); + return ret; + } + + ret = ttm_bo_vmap(tbo, &lbo->map); + if (ret) { + drm_err(obj->dev, "ttm bo vmap failed\n"); + lsdc_bo_unpin(lbo); + return ret; + } + + lbo->vmap_count = 1; + +out: + *map = lbo->map; + + return 0; +} + +static void lsdc_gem_object_vunmap(struct drm_gem_object *obj, struct iosys_map *map) +{ + struct ttm_buffer_object *tbo = to_ttm_bo(obj); + struct lsdc_bo *lbo = to_lsdc_bo(tbo); + + if (unlikely(!lbo->vmap_count)) { + drm_warn(obj->dev, "%p is not mapped\n", lbo); + return; + } + + --lbo->vmap_count; + if (lbo->vmap_count == 0) { + ttm_bo_vunmap(tbo, &lbo->map); + + lsdc_bo_unpin(lbo); + } +} + +static int lsdc_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) +{ + struct ttm_buffer_object *tbo = to_ttm_bo(obj); + int ret; + + ret = ttm_bo_mmap_obj(vma, tbo); + if (unlikely(ret)) { + drm_warn(obj->dev, "mmap %p failed\n", tbo); + return ret; + } + + drm_gem_object_put(obj); + + return 0; +} + +static const struct drm_gem_object_funcs lsdc_gem_object_funcs = { + .free = lsdc_gem_object_free, + .export = drm_gem_prime_export, + .pin = lsdc_gem_prime_pin, + .unpin = lsdc_gem_prime_unpin, + .get_sg_table = lsdc_gem_prime_get_sg_table, + .vmap = lsdc_gem_object_vmap, + .vunmap = lsdc_gem_object_vunmap, + .mmap = lsdc_gem_object_mmap, +}; + +struct drm_gem_object *lsdc_gem_object_create(struct drm_device *ddev, + u32 domain, + size_t size, + bool kerenl, + struct sg_table *sg, + struct dma_resv *resv) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + struct drm_gem_object *gobj; + struct lsdc_bo *lbo; + int ret; + + lbo = lsdc_bo_create(ddev, domain, size, kerenl, sg, resv); + if (IS_ERR(lbo)) { + ret = PTR_ERR(lbo); + return ERR_PTR(ret); + } + + if (!sg) { + /* VRAM is filled with random data */ + lsdc_bo_clear(lbo); + } + + gobj = &lbo->tbo.base; + gobj->funcs = &lsdc_gem_object_funcs; + + /* tracking the BOs we created */ + mutex_lock(&ldev->gem.mutex); + list_add_tail(&lbo->list, &ldev->gem.objects); + mutex_unlock(&ldev->gem.mutex); + + return gobj; +} + +struct drm_gem_object * +lsdc_prime_import_sg_table(struct drm_device *ddev, + struct dma_buf_attachment *attach, + struct sg_table *sg) +{ + struct dma_resv *resv = attach->dmabuf->resv; + u64 size = attach->dmabuf->size; + struct drm_gem_object *gobj; + struct lsdc_bo *lbo; + + dma_resv_lock(resv, NULL); + gobj = lsdc_gem_object_create(ddev, LSDC_GEM_DOMAIN_GTT, size, false, + sg, resv); + dma_resv_unlock(resv); + + if (IS_ERR(gobj)) { + drm_err(ddev, "Failed to import sg table\n"); + return gobj; + } + + lbo = gem_to_lsdc_bo(gobj); + lbo->sharing_count = 1; + + return gobj; +} + +int lsdc_dumb_create(struct drm_file *file, struct drm_device *ddev, + struct drm_mode_create_dumb *args) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_desc *descp = ldev->descp; + u32 domain = LSDC_GEM_DOMAIN_VRAM; + struct drm_gem_object *gobj; + size_t size; + u32 pitch; + u32 handle; + int ret; + + if (!args->width || !args->height) + return -EINVAL; + + if (args->bpp != 32 && args->bpp != 16) + return -EINVAL; + + pitch = args->width * args->bpp / 8; + pitch = ALIGN(pitch, descp->pitch_align); + size = pitch * args->height; + size = ALIGN(size, PAGE_SIZE); + + /* Maximum single bo size allowed is the half vram size available */ + if (size > ldev->vram_size / 2) { + drm_err(ddev, "Requesting(%zuMiB) failed\n", size >> 20); + return -ENOMEM; + } + + gobj = lsdc_gem_object_create(ddev, domain, size, false, NULL, NULL); + if (IS_ERR(gobj)) { + drm_err(ddev, "Failed to create gem object\n"); + return PTR_ERR(gobj); + } + + ret = drm_gem_handle_create(file, gobj, &handle); + + /* drop reference from allocate, handle holds it now */ + drm_gem_object_put(gobj); + if (ret) + return ret; + + args->pitch = pitch; + args->size = size; + args->handle = handle; + + return 0; +} + +int lsdc_dumb_map_offset(struct drm_file *filp, struct drm_device *ddev, + u32 handle, uint64_t *offset) +{ + struct drm_gem_object *gobj; + + gobj = drm_gem_object_lookup(filp, handle); + if (!gobj) + return -ENOENT; + + *offset = drm_vma_node_offset_addr(&gobj->vma_node); + + drm_gem_object_put(gobj); + + return 0; +} + +void lsdc_gem_init(struct drm_device *ddev) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + + mutex_init(&ldev->gem.mutex); + INIT_LIST_HEAD(&ldev->gem.objects); +} + +int lsdc_show_buffer_object(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + struct lsdc_bo *lbo; + unsigned int i; + + mutex_lock(&ldev->gem.mutex); + + i = 0; + + list_for_each_entry(lbo, &ldev->gem.objects, list) { + struct ttm_buffer_object *tbo = &lbo->tbo; + struct ttm_resource *resource = tbo->resource; + + seq_printf(m, "bo[%04u][%p]: size: %8zuKiB %s offset: %8llx\n", + i, lbo, lsdc_bo_size(lbo) >> 10, + lsdc_mem_type_to_str(resource->mem_type), + lsdc_bo_gpu_offset(lbo)); + i++; + } + + mutex_unlock(&ldev->gem.mutex); + + seq_printf(m, "Pinned BO size: VRAM: %zuKiB, GTT: %zu KiB\n", + ldev->vram_pinned_size >> 10, ldev->gtt_pinned_size >> 10); + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_gem.h b/drivers/gpu/drm/loongson/lsdc_gem.h new file mode 100644 index 000000000000..92cbb10e6e13 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_gem.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_GEM_H__ +#define __LSDC_GEM_H__ + +#include <drm/drm_device.h> +#include <drm/drm_gem.h> + +struct drm_gem_object * +lsdc_prime_import_sg_table(struct drm_device *ddev, + struct dma_buf_attachment *attach, + struct sg_table *sg); + +int lsdc_dumb_map_offset(struct drm_file *file, + struct drm_device *dev, + u32 handle, + uint64_t *offset); + +int lsdc_dumb_create(struct drm_file *file, + struct drm_device *ddev, + struct drm_mode_create_dumb *args); + +void lsdc_gem_init(struct drm_device *ddev); +int lsdc_show_buffer_object(struct seq_file *m, void *arg); + +struct drm_gem_object * +lsdc_gem_object_create(struct drm_device *ddev, + u32 domain, + size_t size, + bool kerenl, + struct sg_table *sg, + struct dma_resv *resv); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_gfxpll.c b/drivers/gpu/drm/loongson/lsdc_gfxpll.c new file mode 100644 index 000000000000..249c09d703ad --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_gfxpll.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/delay.h> + +#include <drm/drm_file.h> +#include <drm/drm_managed.h> +#include <drm/drm_print.h> + +#include "lsdc_drv.h" + +/* + * GFX PLL is the PLL used by DC, GMC and GPU, the structure of the GFX PLL + * may suffer from change across chip variants. + * + * + * +-------------+ sel_out_dc + * +----| / div_out_0 | _____/ _____ DC + * | +-------------+ + * refclk +---------+ +-------+ | +-------------+ sel_out_gmc + * ---+---> | div_ref | ---> | loopc | --+--> | / div_out_1 | _____/ _____ GMC + * | +---------+ +-------+ | +-------------+ + * | / * | +-------------+ sel_out_gpu + * | +----| / div_out_2 | _____/ _____ GPU + * | +-------------+ + * | ^ + * | | + * +--------------------------- bypass ----------------------+ + */ + +struct loongson_gfxpll_bitmap { + /* Byte 0 ~ Byte 3 */ + unsigned div_out_dc : 7; /* 6 : 0 DC output clock divider */ + unsigned div_out_gmc : 7; /* 13 : 7 GMC output clock divider */ + unsigned div_out_gpu : 7; /* 20 : 14 GPU output clock divider */ + unsigned loopc : 9; /* 29 : 21 clock multiplier */ + unsigned _reserved_1_ : 2; /* 31 : 30 */ + + /* Byte 4 ~ Byte 7 */ + unsigned div_ref : 7; /* 38 : 32 Input clock divider */ + unsigned locked : 1; /* 39 PLL locked indicator */ + unsigned sel_out_dc : 1; /* 40 dc output clk enable */ + unsigned sel_out_gmc : 1; /* 41 gmc output clk enable */ + unsigned sel_out_gpu : 1; /* 42 gpu output clk enable */ + unsigned set_param : 1; /* 43 Trigger the update */ + unsigned bypass : 1; /* 44 */ + unsigned powerdown : 1; /* 45 */ + unsigned _reserved_2_ : 18; /* 46 : 63 no use */ +}; + +union loongson_gfxpll_reg_bitmap { + struct loongson_gfxpll_bitmap bitmap; + u32 w[2]; + u64 d; +}; + +static void __gfxpll_rreg(struct loongson_gfxpll *this, + union loongson_gfxpll_reg_bitmap *reg) +{ +#if defined(CONFIG_64BIT) + reg->d = readq(this->mmio); +#else + reg->w[0] = readl(this->mmio); + reg->w[1] = readl(this->mmio + 4); +#endif +} + +/* Update new parameters to the hardware */ + +static int loongson_gfxpll_update(struct loongson_gfxpll * const this, + struct loongson_gfxpll_parms const *pin) +{ + /* None, TODO */ + + return 0; +} + +static void loongson_gfxpll_get_rates(struct loongson_gfxpll * const this, + unsigned int *dc, + unsigned int *gmc, + unsigned int *gpu) +{ + struct loongson_gfxpll_parms *pparms = &this->parms; + union loongson_gfxpll_reg_bitmap gfxpll_reg; + unsigned int pre_output; + unsigned int dc_mhz; + unsigned int gmc_mhz; + unsigned int gpu_mhz; + + __gfxpll_rreg(this, &gfxpll_reg); + + pparms->div_ref = gfxpll_reg.bitmap.div_ref; + pparms->loopc = gfxpll_reg.bitmap.loopc; + + pparms->div_out_dc = gfxpll_reg.bitmap.div_out_dc; + pparms->div_out_gmc = gfxpll_reg.bitmap.div_out_gmc; + pparms->div_out_gpu = gfxpll_reg.bitmap.div_out_gpu; + + pre_output = pparms->ref_clock / pparms->div_ref * pparms->loopc; + + dc_mhz = pre_output / pparms->div_out_dc / 1000; + gmc_mhz = pre_output / pparms->div_out_gmc / 1000; + gpu_mhz = pre_output / pparms->div_out_gpu / 1000; + + if (dc) + *dc = dc_mhz; + + if (gmc) + *gmc = gmc_mhz; + + if (gpu) + *gpu = gpu_mhz; +} + +static void loongson_gfxpll_print(struct loongson_gfxpll * const this, + struct drm_printer *p, + bool verbose) +{ + struct loongson_gfxpll_parms *parms = &this->parms; + unsigned int dc, gmc, gpu; + + if (verbose) { + drm_printf(p, "reference clock: %u\n", parms->ref_clock); + drm_printf(p, "div_ref = %u\n", parms->div_ref); + drm_printf(p, "loopc = %u\n", parms->loopc); + + drm_printf(p, "div_out_dc = %u\n", parms->div_out_dc); + drm_printf(p, "div_out_gmc = %u\n", parms->div_out_gmc); + drm_printf(p, "div_out_gpu = %u\n", parms->div_out_gpu); + } + + this->funcs->get_rates(this, &dc, &gmc, &gpu); + + drm_printf(p, "dc: %uMHz, gmc: %uMHz, gpu: %uMHz\n", dc, gmc, gpu); +} + +/* GFX (DC, GPU, GMC) PLL initialization and destroy function */ + +static void loongson_gfxpll_fini(struct drm_device *ddev, void *data) +{ + struct loongson_gfxpll *this = (struct loongson_gfxpll *)data; + + iounmap(this->mmio); + + kfree(this); +} + +static int loongson_gfxpll_init(struct loongson_gfxpll * const this) +{ + struct loongson_gfxpll_parms *pparms = &this->parms; + struct drm_printer printer = drm_info_printer(this->ddev->dev); + + pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ; + + this->mmio = ioremap(this->reg_base, this->reg_size); + if (IS_ERR_OR_NULL(this->mmio)) + return -ENOMEM; + + this->funcs->print(this, &printer, false); + + return 0; +} + +static const struct loongson_gfxpll_funcs lsdc_gmc_gpu_funcs = { + .init = loongson_gfxpll_init, + .update = loongson_gfxpll_update, + .get_rates = loongson_gfxpll_get_rates, + .print = loongson_gfxpll_print, +}; + +int loongson_gfxpll_create(struct drm_device *ddev, + struct loongson_gfxpll **ppout) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + const struct loongson_gfx_desc *gfx = to_loongson_gfx(ldev->descp); + struct loongson_gfxpll *this; + int ret; + + this = kzalloc(sizeof(*this), GFP_KERNEL); + if (IS_ERR_OR_NULL(this)) + return -ENOMEM; + + this->ddev = ddev; + this->reg_size = gfx->gfxpll.reg_size; + this->reg_base = gfx->conf_reg_base + gfx->gfxpll.reg_offset; + this->funcs = &lsdc_gmc_gpu_funcs; + + ret = this->funcs->init(this); + if (unlikely(ret)) { + kfree(this); + return ret; + } + + *ppout = this; + + return drmm_add_action_or_reset(ddev, loongson_gfxpll_fini, this); +} diff --git a/drivers/gpu/drm/loongson/lsdc_gfxpll.h b/drivers/gpu/drm/loongson/lsdc_gfxpll.h new file mode 100644 index 000000000000..9d59cbfc145d --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_gfxpll.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_GFXPLL_H__ +#define __LSDC_GFXPLL_H__ + +#include <drm/drm_device.h> + +struct loongson_gfxpll; + +struct loongson_gfxpll_parms { + unsigned int ref_clock; + unsigned int div_ref; + unsigned int loopc; + unsigned int div_out_dc; + unsigned int div_out_gmc; + unsigned int div_out_gpu; +}; + +struct loongson_gfxpll_funcs { + int (*init)(struct loongson_gfxpll * const this); + + int (*update)(struct loongson_gfxpll * const this, + struct loongson_gfxpll_parms const *pin); + + void (*get_rates)(struct loongson_gfxpll * const this, + unsigned int *dc, unsigned int *gmc, unsigned int *gpu); + + void (*print)(struct loongson_gfxpll * const this, + struct drm_printer *printer, bool verbose); +}; + +struct loongson_gfxpll { + struct drm_device *ddev; + void __iomem *mmio; + + /* PLL register offset */ + u32 reg_base; + /* PLL register size in bytes */ + u32 reg_size; + + const struct loongson_gfxpll_funcs *funcs; + + struct loongson_gfxpll_parms parms; +}; + +int loongson_gfxpll_create(struct drm_device *ddev, + struct loongson_gfxpll **ppout); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_i2c.c b/drivers/gpu/drm/loongson/lsdc_i2c.c new file mode 100644 index 000000000000..9625d0b1d0b4 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_i2c.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <drm/drm_managed.h> + +#include "lsdc_drv.h" +#include "lsdc_output.h" + +/* + * __lsdc_gpio_i2c_set - set the state of a gpio pin indicated by mask + * @mask: gpio pin mask + * @state: "0" for low, "1" for high + */ +static void __lsdc_gpio_i2c_set(struct lsdc_i2c * const li2c, int mask, int state) +{ + struct lsdc_device *ldev = to_lsdc(li2c->ddev); + unsigned long flags; + u8 val; + + spin_lock_irqsave(&ldev->reglock, flags); + + if (state) { + /* + * Setting this pin as input directly, write 1 for input. + * The external pull-up resistor will pull the level up + */ + val = readb(li2c->dir_reg); + val |= mask; + writeb(val, li2c->dir_reg); + } else { + /* First set this pin as output, write 0 for output */ + val = readb(li2c->dir_reg); + val &= ~mask; + writeb(val, li2c->dir_reg); + + /* Then, make this pin output 0 */ + val = readb(li2c->dat_reg); + val &= ~mask; + writeb(val, li2c->dat_reg); + } + + spin_unlock_irqrestore(&ldev->reglock, flags); +} + +/* + * __lsdc_gpio_i2c_get - read value back from the gpio pin indicated by mask + * @mask: gpio pin mask + * return "0" for low, "1" for high + */ +static int __lsdc_gpio_i2c_get(struct lsdc_i2c * const li2c, int mask) +{ + struct lsdc_device *ldev = to_lsdc(li2c->ddev); + unsigned long flags; + u8 val; + + spin_lock_irqsave(&ldev->reglock, flags); + + /* First set this pin as input */ + val = readb(li2c->dir_reg); + val |= mask; + writeb(val, li2c->dir_reg); + + /* Then get level state from this pin */ + val = readb(li2c->dat_reg); + + spin_unlock_irqrestore(&ldev->reglock, flags); + + return (val & mask) ? 1 : 0; +} + +static void lsdc_gpio_i2c_set_sda(void *i2c, int state) +{ + struct lsdc_i2c * const li2c = (struct lsdc_i2c *)i2c; + /* set state on the li2c->sda pin */ + return __lsdc_gpio_i2c_set(li2c, li2c->sda, state); +} + +static void lsdc_gpio_i2c_set_scl(void *i2c, int state) +{ + struct lsdc_i2c * const li2c = (struct lsdc_i2c *)i2c; + /* set state on the li2c->scl pin */ + return __lsdc_gpio_i2c_set(li2c, li2c->scl, state); +} + +static int lsdc_gpio_i2c_get_sda(void *i2c) +{ + struct lsdc_i2c * const li2c = (struct lsdc_i2c *)i2c; + /* read value from the li2c->sda pin */ + return __lsdc_gpio_i2c_get(li2c, li2c->sda); +} + +static int lsdc_gpio_i2c_get_scl(void *i2c) +{ + struct lsdc_i2c * const li2c = (struct lsdc_i2c *)i2c; + /* read the value from the li2c->scl pin */ + return __lsdc_gpio_i2c_get(li2c, li2c->scl); +} + +static void lsdc_destroy_i2c(struct drm_device *ddev, void *data) +{ + struct lsdc_i2c *li2c = (struct lsdc_i2c *)data; + + if (li2c) { + i2c_del_adapter(&li2c->adapter); + kfree(li2c); + } +} + +/* + * The DC in ls7a1000/ls7a2000/ls2k2000 has builtin gpio hardware + * + * @reg_base: gpio reg base + * @index: output channel index, 0 for PIPE0, 1 for PIPE1 + */ +int lsdc_create_i2c_chan(struct drm_device *ddev, + struct lsdc_display_pipe *dispipe, + unsigned int index) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + struct i2c_adapter *adapter; + struct lsdc_i2c *li2c; + int ret; + + li2c = kzalloc(sizeof(*li2c), GFP_KERNEL); + if (!li2c) + return -ENOMEM; + + dispipe->li2c = li2c; + + if (index == 0) { + li2c->sda = 0x01; /* pin 0 */ + li2c->scl = 0x02; /* pin 1 */ + } else if (index == 1) { + li2c->sda = 0x04; /* pin 2 */ + li2c->scl = 0x08; /* pin 3 */ + } else { + return -ENOENT; + } + + li2c->ddev = ddev; + li2c->dir_reg = ldev->reg_base + LS7A_DC_GPIO_DIR_REG; + li2c->dat_reg = ldev->reg_base + LS7A_DC_GPIO_DAT_REG; + + li2c->bit.setsda = lsdc_gpio_i2c_set_sda; + li2c->bit.setscl = lsdc_gpio_i2c_set_scl; + li2c->bit.getsda = lsdc_gpio_i2c_get_sda; + li2c->bit.getscl = lsdc_gpio_i2c_get_scl; + li2c->bit.udelay = 5; + li2c->bit.timeout = usecs_to_jiffies(2200); + li2c->bit.data = li2c; + + adapter = &li2c->adapter; + adapter->algo_data = &li2c->bit; + adapter->owner = THIS_MODULE; + adapter->class = I2C_CLASS_DDC; + adapter->dev.parent = ddev->dev; + adapter->nr = -1; + + snprintf(adapter->name, sizeof(adapter->name), "lsdc-i2c%u", index); + + i2c_set_adapdata(adapter, li2c); + + ret = i2c_bit_add_bus(adapter); + if (ret) { + kfree(li2c); + return ret; + } + + ret = drmm_add_action_or_reset(ddev, lsdc_destroy_i2c, li2c); + if (ret) + return ret; + + drm_info(ddev, "%s(sda pin mask=%u, scl pin mask=%u) created\n", + adapter->name, li2c->sda, li2c->scl); + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_i2c.h b/drivers/gpu/drm/loongson/lsdc_i2c.h new file mode 100644 index 000000000000..88cd1a1817a5 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_i2c.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_I2C_H__ +#define __LSDC_I2C_H__ + +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +struct lsdc_i2c { + struct i2c_adapter adapter; + struct i2c_algo_bit_data bit; + struct drm_device *ddev; + void __iomem *dir_reg; + void __iomem *dat_reg; + /* pin bit mask */ + u8 sda; + u8 scl; +}; + +struct lsdc_display_pipe; + +int lsdc_create_i2c_chan(struct drm_device *ddev, + struct lsdc_display_pipe *dispipe, + unsigned int index); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_irq.c b/drivers/gpu/drm/loongson/lsdc_irq.c new file mode 100644 index 000000000000..efdc4d10792d --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_irq.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <drm/drm_vblank.h> + +#include "lsdc_irq.h" + +/* + * For the DC in LS7A2000, clearing interrupt status is achieved by + * write "1" to LSDC_INT_REG. + * + * For the DC in LS7A1000, clear interrupt status is achieved by write "0" + * to LSDC_INT_REG. + * + * Two different hardware engineers modify it as their will. + */ + +irqreturn_t ls7a2000_dc_irq_handler(int irq, void *arg) +{ + struct drm_device *ddev = arg; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 val; + + /* Read the interrupt status */ + val = lsdc_rreg32(ldev, LSDC_INT_REG); + if ((val & INT_STATUS_MASK) == 0) { + drm_warn(ddev, "no interrupt occurs\n"); + return IRQ_NONE; + } + + ldev->irq_status = val; + + /* write "1" to clear the interrupt status */ + lsdc_wreg32(ldev, LSDC_INT_REG, val); + + if (ldev->irq_status & INT_CRTC0_VSYNC) + drm_handle_vblank(ddev, 0); + + if (ldev->irq_status & INT_CRTC1_VSYNC) + drm_handle_vblank(ddev, 1); + + return IRQ_HANDLED; +} + +/* For the DC in LS7A1000 and LS2K1000 */ +irqreturn_t ls7a1000_dc_irq_handler(int irq, void *arg) +{ + struct drm_device *ddev = arg; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 val; + + /* Read the interrupt status */ + val = lsdc_rreg32(ldev, LSDC_INT_REG); + if ((val & INT_STATUS_MASK) == 0) { + drm_warn(ddev, "no interrupt occurs\n"); + return IRQ_NONE; + } + + ldev->irq_status = val; + + /* write "0" to clear the interrupt status */ + val &= ~(INT_CRTC0_VSYNC | INT_CRTC1_VSYNC); + lsdc_wreg32(ldev, LSDC_INT_REG, val); + + if (ldev->irq_status & INT_CRTC0_VSYNC) + drm_handle_vblank(ddev, 0); + + if (ldev->irq_status & INT_CRTC1_VSYNC) + drm_handle_vblank(ddev, 1); + + return IRQ_HANDLED; +} diff --git a/drivers/gpu/drm/loongson/lsdc_irq.h b/drivers/gpu/drm/loongson/lsdc_irq.h new file mode 100644 index 000000000000..726cb3018b89 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_irq.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_IRQ_H__ +#define __LSDC_IRQ_H__ + +#include <linux/irqreturn.h> + +#include "lsdc_drv.h" + +irqreturn_t ls7a1000_dc_irq_handler(int irq, void *arg); +irqreturn_t ls7a2000_dc_irq_handler(int irq, void *arg); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_output.h b/drivers/gpu/drm/loongson/lsdc_output.h new file mode 100644 index 000000000000..097789051a1d --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_output.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_OUTPUT_H__ +#define __LSDC_OUTPUT_H__ + +#include "lsdc_drv.h" + +int ls7a1000_output_init(struct drm_device *ddev, + struct lsdc_display_pipe *dispipe, + struct i2c_adapter *ddc, + unsigned int index); + +int ls7a2000_output_init(struct drm_device *ldev, + struct lsdc_display_pipe *dispipe, + struct i2c_adapter *ddc, + unsigned int index); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_output_7a1000.c b/drivers/gpu/drm/loongson/lsdc_output_7a1000.c new file mode 100644 index 000000000000..6fc8dd1c7d9a --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_output_7a1000.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_probe_helper.h> + +#include "lsdc_drv.h" +#include "lsdc_output.h" + +/* + * The display controller in the LS7A1000 exports two DVO interfaces, thus + * external encoder is required, except connected to the DPI panel directly. + * + * ___________________ _________ + * | -------| | | + * | CRTC0 --> | DVO0 ----> Encoder0 ---> Connector0 ---> | Display | + * | _ _ -------| ^ ^ |_________| + * | | | | | +------+ | | | + * | |_| |_| | i2c6 | <--------+-------------+ + * | +------+ | + * | | + * | DC in LS7A1000 | + * | | + * | _ _ +------+ | + * | | | | | | i2c7 | <--------+-------------+ + * | |_| |_| +------+ | | | _________ + * | -------| | | | | + * | CRTC1 --> | DVO1 ----> Encoder1 ---> Connector1 ---> | Panel | + * | -------| |_________| + * |___________________| + * + * Currently, we assume the external encoders connected to the DVO are + * transparent. Loongson's DVO interface can directly drive RGB888 panels. + * + * TODO: Add support for non-transparent encoders + */ + +static int ls7a1000_dpi_connector_get_modes(struct drm_connector *conn) +{ + unsigned int num = 0; + struct edid *edid; + + if (conn->ddc) { + edid = drm_get_edid(conn, conn->ddc); + if (edid) { + drm_connector_update_edid_property(conn, edid); + num = drm_add_edid_modes(conn, edid); + kfree(edid); + } + + return num; + } + + num = drm_add_modes_noedid(conn, 1920, 1200); + + drm_set_preferred_mode(conn, 1024, 768); + + return num; +} + +static struct drm_encoder * +ls7a1000_dpi_connector_get_best_encoder(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct lsdc_output *output = connector_to_lsdc_output(connector); + + return &output->encoder; +} + +static const struct drm_connector_helper_funcs +ls7a1000_dpi_connector_helpers = { + .atomic_best_encoder = ls7a1000_dpi_connector_get_best_encoder, + .get_modes = ls7a1000_dpi_connector_get_modes, +}; + +static enum drm_connector_status +ls7a1000_dpi_connector_detect(struct drm_connector *connector, bool force) +{ + struct i2c_adapter *ddc = connector->ddc; + + if (ddc) { + if (drm_probe_ddc(ddc)) + return connector_status_connected; + + return connector_status_disconnected; + } + + return connector_status_unknown; +} + +static const struct drm_connector_funcs ls7a1000_dpi_connector_funcs = { + .detect = ls7a1000_dpi_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state +}; + +static void ls7a1000_pipe0_encoder_reset(struct drm_encoder *encoder) +{ + struct drm_device *ddev = encoder->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + + /* + * We need this for S3 support, screen will not lightup if don't set + * this register correctly. + */ + lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG, + PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN); +} + +static void ls7a1000_pipe1_encoder_reset(struct drm_encoder *encoder) +{ + struct drm_device *ddev = encoder->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + + /* + * We need this for S3 support, screen will not lightup if don't set + * this register correctly. + */ + + /* DVO */ + lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG, + BIT(31) | PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN); +} + +static const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = { + { + .reset = ls7a1000_pipe0_encoder_reset, + .destroy = drm_encoder_cleanup, + }, + { + .reset = ls7a1000_pipe1_encoder_reset, + .destroy = drm_encoder_cleanup, + }, +}; + +int ls7a1000_output_init(struct drm_device *ddev, + struct lsdc_display_pipe *dispipe, + struct i2c_adapter *ddc, + unsigned int index) +{ + struct lsdc_output *output = &dispipe->output; + struct drm_encoder *encoder = &output->encoder; + struct drm_connector *connector = &output->connector; + int ret; + + ret = drm_encoder_init(ddev, encoder, &ls7a1000_encoder_funcs[index], + DRM_MODE_ENCODER_TMDS, "encoder-%u", index); + if (ret) + return ret; + + encoder->possible_crtcs = BIT(index); + + ret = drm_connector_init_with_ddc(ddev, connector, + &ls7a1000_dpi_connector_funcs, + DRM_MODE_CONNECTOR_DPI, ddc); + if (ret) + return ret; + + drm_info(ddev, "display pipe-%u has a DVO\n", index); + + drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers); + + drm_connector_attach_encoder(connector, encoder); + + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_output_7a2000.c b/drivers/gpu/drm/loongson/lsdc_output_7a2000.c new file mode 100644 index 000000000000..ce3dabec887e --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_output_7a2000.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/delay.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_debugfs.h> +#include <drm/drm_edid.h> +#include <drm/drm_probe_helper.h> + +#include "lsdc_drv.h" +#include "lsdc_output.h" + +/* + * The display controller in LS7A2000 has two display pipes + * Display pipe 0 is attached with a built-in transparent VGA encoder and + * a built-in HDMI encoder. + * Display pipe 1 has only one built-in HDMI encoder connected. + * ______________________ _____________ + * | +-----+ | | | + * | CRTC0 -+--> | VGA | ----> VGA Connector ---> | VGA Monitor |<---+ + * | | +-----+ | |_____________| | + * | | | ______________ | + * | | +------+ | | | | + * | +--> | HDMI | ----> HDMI Connector --> | HDMI Monitor |<--+ + * | +------+ | |______________| | + * | +------+ | | + * | | i2c6 | <-------------------------------------------+ + * | +------+ | + * | | + * | DC in LS7A2000 | + * | | + * | +------+ | + * | | i2c7 | <--------------------------------+ + * | +------+ | | + * | | ______|_______ + * | +------+ | | | + * | CRTC1 ---> | HDMI | ----> HDMI Connector ---> | HDMI Monitor | + * | +------+ | |______________| + * |______________________| + */ + +static int ls7a2000_connector_get_modes(struct drm_connector *connector) +{ + unsigned int num = 0; + struct edid *edid; + + if (connector->ddc) { + edid = drm_get_edid(connector, connector->ddc); + if (edid) { + drm_connector_update_edid_property(connector, edid); + num = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + return num; + } + + num = drm_add_modes_noedid(connector, 1920, 1200); + + drm_set_preferred_mode(connector, 1024, 768); + + return num; +} + +static struct drm_encoder * +ls7a2000_connector_get_best_encoder(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct lsdc_output *output = connector_to_lsdc_output(connector); + + return &output->encoder; +} + +static const struct drm_connector_helper_funcs ls7a2000_connector_helpers = { + .atomic_best_encoder = ls7a2000_connector_get_best_encoder, + .get_modes = ls7a2000_connector_get_modes, +}; + +/* debugfs */ + +#define LSDC_HDMI_REG(i, reg) { \ + .name = __stringify_1(LSDC_HDMI##i##_##reg##_REG), \ + .offset = LSDC_HDMI##i##_##reg##_REG, \ +} + +static const struct lsdc_reg32 ls7a2000_hdmi0_encoder_regs[] = { + LSDC_HDMI_REG(0, ZONE), + LSDC_HDMI_REG(0, INTF_CTRL), + LSDC_HDMI_REG(0, PHY_CTRL), + LSDC_HDMI_REG(0, PHY_PLL), + LSDC_HDMI_REG(0, AVI_INFO_CRTL), + LSDC_HDMI_REG(0, PHY_CAL), + LSDC_HDMI_REG(0, AUDIO_PLL_LO), + LSDC_HDMI_REG(0, AUDIO_PLL_HI), + {NULL, 0}, /* MUST be {NULL, 0} terminated */ +}; + +static const struct lsdc_reg32 ls7a2000_hdmi1_encoder_regs[] = { + LSDC_HDMI_REG(1, ZONE), + LSDC_HDMI_REG(1, INTF_CTRL), + LSDC_HDMI_REG(1, PHY_CTRL), + LSDC_HDMI_REG(1, PHY_PLL), + LSDC_HDMI_REG(1, AVI_INFO_CRTL), + LSDC_HDMI_REG(1, PHY_CAL), + LSDC_HDMI_REG(1, AUDIO_PLL_LO), + LSDC_HDMI_REG(1, AUDIO_PLL_HI), + {NULL, 0}, /* MUST be {NULL, 0} terminated */ +}; + +static int ls7a2000_hdmi_encoder_regs_show(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_reg32 *preg; + + preg = (const struct lsdc_reg32 *)node->info_ent->data; + + while (preg->name) { + u32 offset = preg->offset; + + seq_printf(m, "%s (0x%04x): 0x%08x\n", + preg->name, offset, lsdc_rreg32(ldev, offset)); + ++preg; + } + + return 0; +} + +static const struct drm_info_list ls7a2000_hdmi0_debugfs_files[] = { + { "regs", ls7a2000_hdmi_encoder_regs_show, 0, (void *)ls7a2000_hdmi0_encoder_regs }, +}; + +static const struct drm_info_list ls7a2000_hdmi1_debugfs_files[] = { + { "regs", ls7a2000_hdmi_encoder_regs_show, 0, (void *)ls7a2000_hdmi1_encoder_regs }, +}; + +static void ls7a2000_hdmi0_late_register(struct drm_connector *connector, + struct dentry *root) +{ + struct drm_device *ddev = connector->dev; + struct drm_minor *minor = ddev->primary; + + drm_debugfs_create_files(ls7a2000_hdmi0_debugfs_files, + ARRAY_SIZE(ls7a2000_hdmi0_debugfs_files), + root, minor); +} + +static void ls7a2000_hdmi1_late_register(struct drm_connector *connector, + struct dentry *root) +{ + struct drm_device *ddev = connector->dev; + struct drm_minor *minor = ddev->primary; + + drm_debugfs_create_files(ls7a2000_hdmi1_debugfs_files, + ARRAY_SIZE(ls7a2000_hdmi1_debugfs_files), + root, minor); +} + +/* monitor present detection */ + +static enum drm_connector_status +ls7a2000_hdmi0_vga_connector_detect(struct drm_connector *connector, bool force) +{ + struct drm_device *ddev = connector->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 val; + + val = lsdc_rreg32(ldev, LSDC_HDMI_HPD_STATUS_REG); + + if (val & HDMI0_HPD_FLAG) + return connector_status_connected; + + if (connector->ddc) { + if (drm_probe_ddc(connector->ddc)) + return connector_status_connected; + + return connector_status_disconnected; + } + + return connector_status_unknown; +} + +static enum drm_connector_status +ls7a2000_hdmi1_connector_detect(struct drm_connector *connector, bool force) +{ + struct lsdc_device *ldev = to_lsdc(connector->dev); + u32 val; + + val = lsdc_rreg32(ldev, LSDC_HDMI_HPD_STATUS_REG); + + if (val & HDMI1_HPD_FLAG) + return connector_status_connected; + + return connector_status_disconnected; +} + +static const struct drm_connector_funcs ls7a2000_hdmi_connector_funcs[2] = { + { + .detect = ls7a2000_hdmi0_vga_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .debugfs_init = ls7a2000_hdmi0_late_register, + }, + { + .detect = ls7a2000_hdmi1_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .debugfs_init = ls7a2000_hdmi1_late_register, + }, +}; + +/* Even though some board has only one hdmi on display pipe 1, + * We still need hook lsdc_encoder_funcs up on display pipe 0, + * This is because we need its reset() callback get called, to + * set the LSDC_HDMIx_CTRL_REG using software gpio emulated i2c. + * Otherwise, the firmware may set LSDC_HDMIx_CTRL_REG blindly. + */ +static void ls7a2000_hdmi0_encoder_reset(struct drm_encoder *encoder) +{ + struct drm_device *ddev = encoder->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 val; + + val = PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN; + lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG, val); + + /* using software gpio emulated i2c */ + val = lsdc_rreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG); + val &= ~HW_I2C_EN; + lsdc_wreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, val); + + /* help the hdmi phy to get out of reset state */ + lsdc_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, HDMI_PHY_RESET_N); + + mdelay(20); + + drm_dbg(ddev, "HDMI-0 Reset\n"); +} + +static void ls7a2000_hdmi1_encoder_reset(struct drm_encoder *encoder) +{ + struct drm_device *ddev = encoder->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 val; + + val = PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN; + lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG, val); + + /* using software gpio emulated i2c */ + val = lsdc_rreg32(ldev, LSDC_HDMI1_INTF_CTRL_REG); + val &= ~HW_I2C_EN; + lsdc_wreg32(ldev, LSDC_HDMI1_INTF_CTRL_REG, val); + + /* help the hdmi phy to get out of reset state */ + lsdc_wreg32(ldev, LSDC_HDMI1_PHY_CTRL_REG, HDMI_PHY_RESET_N); + + mdelay(20); + + drm_dbg(ddev, "HDMI-1 Reset\n"); +} + +static const struct drm_encoder_funcs ls7a2000_encoder_funcs[2] = { + { + .reset = ls7a2000_hdmi0_encoder_reset, + .destroy = drm_encoder_cleanup, + }, + { + .reset = ls7a2000_hdmi1_encoder_reset, + .destroy = drm_encoder_cleanup, + }, +}; + +static int ls7a2000_hdmi_set_avi_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct lsdc_output *output = encoder_to_lsdc_output(encoder); + struct lsdc_display_pipe *dispipe = output_to_display_pipe(output); + unsigned int index = dispipe->index; + struct drm_device *ddev = encoder->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + struct hdmi_avi_infoframe infoframe; + u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; + unsigned char *ptr = &buffer[HDMI_INFOFRAME_HEADER_SIZE]; + unsigned int content0, content1, content2, content3; + int err; + + err = drm_hdmi_avi_infoframe_from_display_mode(&infoframe, + &output->connector, + mode); + if (err < 0) { + drm_err(ddev, "failed to setup AVI infoframe: %d\n", err); + return err; + } + + /* Fixed infoframe configuration not linked to the mode */ + infoframe.colorspace = HDMI_COLORSPACE_RGB; + infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; + infoframe.colorimetry = HDMI_COLORIMETRY_NONE; + + err = hdmi_avi_infoframe_pack(&infoframe, buffer, sizeof(buffer)); + if (err < 0) { + drm_err(ddev, "failed to pack AVI infoframe: %d\n", err); + return err; + } + + content0 = *(unsigned int *)ptr; + content1 = *(ptr + 4); + content2 = *(unsigned int *)(ptr + 5); + content3 = *(unsigned int *)(ptr + 9); + + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT0, index, content0); + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT1, index, content1); + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT2, index, content2); + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT3, index, content3); + + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_INFO_CRTL_REG, index, + AVI_PKT_ENABLE | AVI_PKT_UPDATE); + + drm_dbg(ddev, "Update HDMI-%u avi infoframe\n", index); + + return 0; +} + +static void ls7a2000_hdmi_atomic_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct lsdc_output *output = encoder_to_lsdc_output(encoder); + struct lsdc_display_pipe *dispipe = output_to_display_pipe(output); + unsigned int index = dispipe->index; + struct drm_device *ddev = encoder->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 val; + + /* Disable the hdmi phy */ + val = lsdc_pipe_rreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, index); + val &= ~HDMI_PHY_EN; + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, index, val); + + /* Disable the hdmi interface */ + val = lsdc_pipe_rreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, index); + val &= ~HDMI_INTERFACE_EN; + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, index, val); + + drm_dbg(ddev, "HDMI-%u disabled\n", index); +} + +static void ls7a2000_hdmi_atomic_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct drm_device *ddev = encoder->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + struct lsdc_output *output = encoder_to_lsdc_output(encoder); + struct lsdc_display_pipe *dispipe = output_to_display_pipe(output); + unsigned int index = dispipe->index; + u32 val; + + /* datasheet say it should larger than 48 */ + val = 64 << HDMI_H_ZONE_IDLE_SHIFT | 64 << HDMI_V_ZONE_IDLE_SHIFT; + + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_ZONE_REG, index, val); + + val = HDMI_PHY_TERM_STATUS | + HDMI_PHY_TERM_DET_EN | + HDMI_PHY_TERM_H_EN | + HDMI_PHY_TERM_L_EN | + HDMI_PHY_RESET_N | + HDMI_PHY_EN; + + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, index, val); + + udelay(2); + + val = HDMI_CTL_PERIOD_MODE | + HDMI_AUDIO_EN | + HDMI_PACKET_EN | + HDMI_INTERFACE_EN | + (8 << HDMI_VIDEO_PREAMBLE_SHIFT); + + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, index, val); + + drm_dbg(ddev, "HDMI-%u enabled\n", index); +} + +/* + * Fout = M * Fin + * + * M = (4 * LF) / (IDF * ODF) + * + * IDF: Input Division Factor + * ODF: Output Division Factor + * LF: Loop Factor + * M: Required Mult + * + * +--------------------------------------------------------+ + * | Fin (kHZ) | M | IDF | LF | ODF | Fout(Mhz) | + * |-------------------+----+-----+----+-----+--------------| + * | 170000 ~ 340000 | 10 | 16 | 40 | 1 | 1700 ~ 3400 | + * | 85000 ~ 170000 | 10 | 8 | 40 | 2 | 850 ~ 1700 | + * | 42500 ~ 85000 | 10 | 4 | 40 | 4 | 425 ~ 850 | + * | 21250 ~ 42500 | 10 | 2 | 40 | 8 | 212.5 ~ 425 | + * | 20000 ~ 21250 | 10 | 1 | 40 | 16 | 200 ~ 212.5 | + * +--------------------------------------------------------+ + */ +static void ls7a2000_hdmi_phy_pll_config(struct lsdc_device *ldev, + int fin, + unsigned int index) +{ + struct drm_device *ddev = &ldev->base; + int count = 0; + u32 val; + + /* Firstly, disable phy pll */ + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index, 0x0); + + /* + * Most of time, loongson HDMI require M = 10 + * for example, 10 = (4 * 40) / (8 * 2) + * here, write "1" to the ODF will get "2" + */ + + if (fin >= 170000) + val = (16 << HDMI_PLL_IDF_SHIFT) | + (40 << HDMI_PLL_LF_SHIFT) | + (0 << HDMI_PLL_ODF_SHIFT); + else if (fin >= 85000) + val = (8 << HDMI_PLL_IDF_SHIFT) | + (40 << HDMI_PLL_LF_SHIFT) | + (1 << HDMI_PLL_ODF_SHIFT); + else if (fin >= 42500) + val = (4 << HDMI_PLL_IDF_SHIFT) | + (40 << HDMI_PLL_LF_SHIFT) | + (2 << HDMI_PLL_ODF_SHIFT); + else if (fin >= 21250) + val = (2 << HDMI_PLL_IDF_SHIFT) | + (40 << HDMI_PLL_LF_SHIFT) | + (3 << HDMI_PLL_ODF_SHIFT); + else + val = (1 << HDMI_PLL_IDF_SHIFT) | + (40 << HDMI_PLL_LF_SHIFT) | + (4 << HDMI_PLL_ODF_SHIFT); + + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index, val); + + val |= HDMI_PLL_ENABLE; + + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index, val); + + udelay(2); + + drm_dbg(ddev, "Fin of HDMI-%u: %d kHz\n", index, fin); + + /* Wait hdmi phy pll lock */ + do { + val = lsdc_pipe_rreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index); + + if (val & HDMI_PLL_LOCKED) { + drm_dbg(ddev, "Setting HDMI-%u PLL take %d cycles\n", + index, count); + break; + } + ++count; + } while (count < 1000); + + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_CAL_REG, index, 0x0f000ff0); + + if (count >= 1000) + drm_err(ddev, "Setting HDMI-%u PLL failed\n", index); +} + +static void ls7a2000_hdmi_atomic_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct lsdc_output *output = encoder_to_lsdc_output(encoder); + struct lsdc_display_pipe *dispipe = output_to_display_pipe(output); + unsigned int index = dispipe->index; + struct drm_device *ddev = encoder->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + struct drm_display_mode *mode = &crtc_state->mode; + + ls7a2000_hdmi_phy_pll_config(ldev, mode->clock, index); + + ls7a2000_hdmi_set_avi_infoframe(encoder, mode); + + drm_dbg(ddev, "%s modeset finished\n", encoder->name); +} + +static const struct drm_encoder_helper_funcs ls7a2000_encoder_helper_funcs = { + .atomic_disable = ls7a2000_hdmi_atomic_disable, + .atomic_enable = ls7a2000_hdmi_atomic_enable, + .atomic_mode_set = ls7a2000_hdmi_atomic_mode_set, +}; + +/* + * For LS7A2000: + * + * 1) Most of board export one vga + hdmi output interface. + * 2) Yet, Some boards export double hdmi output interface. + * 3) Still have boards export three output(2 hdmi + 1 vga). + * + * So let's hook hdmi helper funcs to all display pipe, don't miss. + * writing hdmi register do no harms. + */ +int ls7a2000_output_init(struct drm_device *ddev, + struct lsdc_display_pipe *dispipe, + struct i2c_adapter *ddc, + unsigned int pipe) +{ + struct lsdc_output *output = &dispipe->output; + struct drm_encoder *encoder = &output->encoder; + struct drm_connector *connector = &output->connector; + int ret; + + ret = drm_encoder_init(ddev, encoder, &ls7a2000_encoder_funcs[pipe], + DRM_MODE_ENCODER_TMDS, "encoder-%u", pipe); + if (ret) + return ret; + + encoder->possible_crtcs = BIT(pipe); + + drm_encoder_helper_add(encoder, &ls7a2000_encoder_helper_funcs); + + ret = drm_connector_init_with_ddc(ddev, connector, + &ls7a2000_hdmi_connector_funcs[pipe], + DRM_MODE_CONNECTOR_HDMIA, ddc); + if (ret) + return ret; + + drm_info(ddev, "display pipe-%u has HDMI %s\n", pipe, pipe ? "" : "and/or VGA"); + + drm_connector_helper_add(connector, &ls7a2000_connector_helpers); + + drm_connector_attach_encoder(connector, encoder); + + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_pixpll.c b/drivers/gpu/drm/loongson/lsdc_pixpll.c new file mode 100644 index 000000000000..04c15b4697e2 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_pixpll.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/delay.h> + +#include <drm/drm_managed.h> + +#include "lsdc_drv.h" + +/* + * The structure of the pixel PLL registers is evolved with times, + * it can be different across different chip also. + */ + +/* size is u64, note that all loongson's cpu is little endian. + * This structure is same for ls7a2000, ls7a1000 and ls2k2000. + */ +struct lsdc_pixpll_reg { + /* Byte 0 ~ Byte 3 */ + unsigned div_out : 7; /* 6 : 0 Output clock divider */ + unsigned _reserved_1_ : 14; /* 20 : 7 */ + unsigned loopc : 9; /* 29 : 21 Clock multiplier */ + unsigned _reserved_2_ : 2; /* 31 : 30 */ + + /* Byte 4 ~ Byte 7 */ + unsigned div_ref : 7; /* 38 : 32 Input clock divider */ + unsigned locked : 1; /* 39 PLL locked indicator */ + unsigned sel_out : 1; /* 40 output clk selector */ + unsigned _reserved_3_ : 2; /* 42 : 41 */ + unsigned set_param : 1; /* 43 Trigger the update */ + unsigned bypass : 1; /* 44 */ + unsigned powerdown : 1; /* 45 */ + unsigned _reserved_4_ : 18; /* 46 : 63 no use */ +}; + +union lsdc_pixpll_reg_bitmap { + struct lsdc_pixpll_reg bitmap; + u32 w[2]; + u64 d; +}; + +struct clk_to_pixpll_parms_lookup_t { + unsigned int clock; /* kHz */ + + unsigned short width; + unsigned short height; + unsigned short vrefresh; + + /* Stores parameters for programming the Hardware PLLs */ + unsigned short div_out; + unsigned short loopc; + unsigned short div_ref; +}; + +static const struct clk_to_pixpll_parms_lookup_t pixpll_parms_table[] = { + {148500, 1920, 1080, 60, 11, 49, 3}, /* 1920x1080@60Hz */ + {141750, 1920, 1080, 60, 11, 78, 5}, /* 1920x1080@60Hz */ + /* 1920x1080@50Hz */ + {174500, 1920, 1080, 75, 17, 89, 3}, /* 1920x1080@75Hz */ + {181250, 2560, 1080, 75, 8, 58, 4}, /* 2560x1080@75Hz */ + {297000, 2560, 1080, 30, 8, 95, 4}, /* 3840x2160@30Hz */ + {301992, 1920, 1080, 100, 10, 151, 5}, /* 1920x1080@100Hz */ + {146250, 1680, 1050, 60, 16, 117, 5}, /* 1680x1050@60Hz */ + {135000, 1280, 1024, 75, 10, 54, 4}, /* 1280x1024@75Hz */ + {119000, 1680, 1050, 60, 20, 119, 5}, /* 1680x1050@60Hz */ + {108000, 1600, 900, 60, 15, 81, 5}, /* 1600x900@60Hz */ + /* 1280x1024@60Hz */ + /* 1280x960@60Hz */ + /* 1152x864@75Hz */ + + {106500, 1440, 900, 60, 19, 81, 4}, /* 1440x900@60Hz */ + {88750, 1440, 900, 60, 16, 71, 5}, /* 1440x900@60Hz */ + {83500, 1280, 800, 60, 17, 71, 5}, /* 1280x800@60Hz */ + {71000, 1280, 800, 60, 20, 71, 5}, /* 1280x800@60Hz */ + + {74250, 1280, 720, 60, 22, 49, 3}, /* 1280x720@60Hz */ + /* 1280x720@50Hz */ + + {78750, 1024, 768, 75, 16, 63, 5}, /* 1024x768@75Hz */ + {75000, 1024, 768, 70, 29, 87, 4}, /* 1024x768@70Hz */ + {65000, 1024, 768, 60, 20, 39, 3}, /* 1024x768@60Hz */ + + {51200, 1024, 600, 60, 25, 64, 5}, /* 1024x600@60Hz */ + + {57284, 832, 624, 75, 24, 55, 4}, /* 832x624@75Hz */ + {49500, 800, 600, 75, 40, 99, 5}, /* 800x600@75Hz */ + {50000, 800, 600, 72, 44, 88, 4}, /* 800x600@72Hz */ + {40000, 800, 600, 60, 30, 36, 3}, /* 800x600@60Hz */ + {36000, 800, 600, 56, 50, 72, 4}, /* 800x600@56Hz */ + {31500, 640, 480, 75, 40, 63, 5}, /* 640x480@75Hz */ + /* 640x480@73Hz */ + + {30240, 640, 480, 67, 62, 75, 4}, /* 640x480@67Hz */ + {27000, 720, 576, 50, 50, 54, 4}, /* 720x576@60Hz */ + {25175, 640, 480, 60, 85, 107, 5}, /* 640x480@60Hz */ + {25200, 640, 480, 60, 50, 63, 5}, /* 640x480@60Hz */ + /* 720x480@60Hz */ +}; + +static void lsdc_pixel_pll_free(struct drm_device *ddev, void *data) +{ + struct lsdc_pixpll *this = (struct lsdc_pixpll *)data; + + iounmap(this->mmio); + + kfree(this->priv); + + drm_dbg(ddev, "pixpll private data freed\n"); +} + +/* + * ioremap the device dependent PLL registers + * + * @this: point to the object where this function is called from + */ +static int lsdc_pixel_pll_setup(struct lsdc_pixpll * const this) +{ + struct lsdc_pixpll_parms *pparms; + + this->mmio = ioremap(this->reg_base, this->reg_size); + if (IS_ERR_OR_NULL(this->mmio)) + return -ENOMEM; + + pparms = kzalloc(sizeof(*pparms), GFP_KERNEL); + if (IS_ERR_OR_NULL(pparms)) + return -ENOMEM; + + pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ; + + this->priv = pparms; + + return drmm_add_action_or_reset(this->ddev, lsdc_pixel_pll_free, this); +} + +/* + * Find a set of pll parameters from a static local table which avoid + * computing the pll parameter eachtime a modeset is triggered. + * + * @this: point to the object where this function is called from + * @clock: the desired output pixel clock, the unit is kHz + * @pout: point to where the parameters to store if found + * + * Return 0 if success, return -1 if not found. + */ +static int lsdc_pixpll_find(struct lsdc_pixpll * const this, + unsigned int clock, + struct lsdc_pixpll_parms *pout) +{ + unsigned int num = ARRAY_SIZE(pixpll_parms_table); + const struct clk_to_pixpll_parms_lookup_t *pt; + unsigned int i; + + for (i = 0; i < num; ++i) { + pt = &pixpll_parms_table[i]; + + if (clock == pt->clock) { + pout->div_ref = pt->div_ref; + pout->loopc = pt->loopc; + pout->div_out = pt->div_out; + + return 0; + } + } + + drm_dbg_kms(this->ddev, "pixel clock %u: miss\n", clock); + + return -1; +} + +/* + * Find a set of pll parameters which have minimal difference with the + * desired pixel clock frequency. It does that by computing all of the + * possible combination. Compute the diff and find the combination with + * minimal diff. + * + * clock_out = refclk / div_ref * loopc / div_out + * + * refclk is determined by the oscillator mounted on motherboard(100MHz + * in almost all board) + * + * @this: point to the object from where this function is called + * @clock: the desired output pixel clock, the unit is kHz + * @pout: point to the out struct of lsdc_pixpll_parms + * + * Return 0 if a set of parameter is found, otherwise return the error + * between clock_kHz we wanted and the most closest candidate with it. + */ +static int lsdc_pixel_pll_compute(struct lsdc_pixpll * const this, + unsigned int clock, + struct lsdc_pixpll_parms *pout) +{ + struct lsdc_pixpll_parms *pparms = this->priv; + unsigned int refclk = pparms->ref_clock; + const unsigned int tolerance = 1000; + unsigned int min = tolerance; + unsigned int div_out, loopc, div_ref; + unsigned int computed; + + if (!lsdc_pixpll_find(this, clock, pout)) + return 0; + + for (div_out = 6; div_out < 64; div_out++) { + for (div_ref = 3; div_ref < 6; div_ref++) { + for (loopc = 6; loopc < 161; loopc++) { + unsigned int diff = 0; + + if (loopc < 12 * div_ref) + continue; + if (loopc > 32 * div_ref) + continue; + + computed = refclk / div_ref * loopc / div_out; + + if (clock >= computed) + diff = clock - computed; + else + diff = computed - clock; + + if (diff < min) { + min = diff; + pparms->div_ref = div_ref; + pparms->div_out = div_out; + pparms->loopc = loopc; + + if (diff == 0) { + *pout = *pparms; + return 0; + } + } + } + } + } + + /* still acceptable */ + if (min < tolerance) { + *pout = *pparms; + return 0; + } + + drm_dbg(this->ddev, "can't find suitable params for %u khz\n", clock); + + return min; +} + +/* Pixel pll hardware related ops, per display pipe */ + +static void __pixpll_rreg(struct lsdc_pixpll *this, + union lsdc_pixpll_reg_bitmap *dst) +{ +#if defined(CONFIG_64BIT) + dst->d = readq(this->mmio); +#else + dst->w[0] = readl(this->mmio); + dst->w[1] = readl(this->mmio + 4); +#endif +} + +static void __pixpll_wreg(struct lsdc_pixpll *this, + union lsdc_pixpll_reg_bitmap *src) +{ +#if defined(CONFIG_64BIT) + writeq(src->d, this->mmio); +#else + writel(src->w[0], this->mmio); + writel(src->w[1], this->mmio + 4); +#endif +} + +static void __pixpll_ops_powerup(struct lsdc_pixpll * const this) +{ + union lsdc_pixpll_reg_bitmap pixpll_reg; + + __pixpll_rreg(this, &pixpll_reg); + + pixpll_reg.bitmap.powerdown = 0; + + __pixpll_wreg(this, &pixpll_reg); +} + +static void __pixpll_ops_powerdown(struct lsdc_pixpll * const this) +{ + union lsdc_pixpll_reg_bitmap pixpll_reg; + + __pixpll_rreg(this, &pixpll_reg); + + pixpll_reg.bitmap.powerdown = 1; + + __pixpll_wreg(this, &pixpll_reg); +} + +static void __pixpll_ops_on(struct lsdc_pixpll * const this) +{ + union lsdc_pixpll_reg_bitmap pixpll_reg; + + __pixpll_rreg(this, &pixpll_reg); + + pixpll_reg.bitmap.sel_out = 1; + + __pixpll_wreg(this, &pixpll_reg); +} + +static void __pixpll_ops_off(struct lsdc_pixpll * const this) +{ + union lsdc_pixpll_reg_bitmap pixpll_reg; + + __pixpll_rreg(this, &pixpll_reg); + + pixpll_reg.bitmap.sel_out = 0; + + __pixpll_wreg(this, &pixpll_reg); +} + +static void __pixpll_ops_bypass(struct lsdc_pixpll * const this) +{ + union lsdc_pixpll_reg_bitmap pixpll_reg; + + __pixpll_rreg(this, &pixpll_reg); + + pixpll_reg.bitmap.bypass = 1; + + __pixpll_wreg(this, &pixpll_reg); +} + +static void __pixpll_ops_unbypass(struct lsdc_pixpll * const this) +{ + union lsdc_pixpll_reg_bitmap pixpll_reg; + + __pixpll_rreg(this, &pixpll_reg); + + pixpll_reg.bitmap.bypass = 0; + + __pixpll_wreg(this, &pixpll_reg); +} + +static void __pixpll_ops_untoggle_param(struct lsdc_pixpll * const this) +{ + union lsdc_pixpll_reg_bitmap pixpll_reg; + + __pixpll_rreg(this, &pixpll_reg); + + pixpll_reg.bitmap.set_param = 0; + + __pixpll_wreg(this, &pixpll_reg); +} + +static void __pixpll_ops_set_param(struct lsdc_pixpll * const this, + struct lsdc_pixpll_parms const *p) +{ + union lsdc_pixpll_reg_bitmap pixpll_reg; + + __pixpll_rreg(this, &pixpll_reg); + + pixpll_reg.bitmap.div_ref = p->div_ref; + pixpll_reg.bitmap.loopc = p->loopc; + pixpll_reg.bitmap.div_out = p->div_out; + + __pixpll_wreg(this, &pixpll_reg); +} + +static void __pixpll_ops_toggle_param(struct lsdc_pixpll * const this) +{ + union lsdc_pixpll_reg_bitmap pixpll_reg; + + __pixpll_rreg(this, &pixpll_reg); + + pixpll_reg.bitmap.set_param = 1; + + __pixpll_wreg(this, &pixpll_reg); +} + +static void __pixpll_ops_wait_locked(struct lsdc_pixpll * const this) +{ + union lsdc_pixpll_reg_bitmap pixpll_reg; + unsigned int counter = 0; + + do { + __pixpll_rreg(this, &pixpll_reg); + + if (pixpll_reg.bitmap.locked) + break; + + ++counter; + } while (counter < 2000); + + drm_dbg(this->ddev, "%u loop waited\n", counter); +} + +/* + * Update the PLL parameters to the PLL hardware + * + * @this: point to the object from which this function is called + * @pin: point to the struct of lsdc_pixpll_parms passed in + * + * return 0 if successful. + */ +static int lsdc_pixpll_update(struct lsdc_pixpll * const this, + struct lsdc_pixpll_parms const *pin) +{ + __pixpll_ops_bypass(this); + + __pixpll_ops_off(this); + + __pixpll_ops_powerdown(this); + + __pixpll_ops_toggle_param(this); + + __pixpll_ops_set_param(this, pin); + + __pixpll_ops_untoggle_param(this); + + __pixpll_ops_powerup(this); + + udelay(2); + + __pixpll_ops_wait_locked(this); + + __pixpll_ops_on(this); + + __pixpll_ops_unbypass(this); + + return 0; +} + +static unsigned int lsdc_pixpll_get_freq(struct lsdc_pixpll * const this) +{ + struct lsdc_pixpll_parms *ppar = this->priv; + union lsdc_pixpll_reg_bitmap pix_pll_reg; + unsigned int freq; + + __pixpll_rreg(this, &pix_pll_reg); + + ppar->div_ref = pix_pll_reg.bitmap.div_ref; + ppar->loopc = pix_pll_reg.bitmap.loopc; + ppar->div_out = pix_pll_reg.bitmap.div_out; + + freq = ppar->ref_clock / ppar->div_ref * ppar->loopc / ppar->div_out; + + return freq; +} + +static void lsdc_pixpll_print(struct lsdc_pixpll * const this, + struct drm_printer *p) +{ + struct lsdc_pixpll_parms *parms = this->priv; + + drm_printf(p, "div_ref: %u, loopc: %u, div_out: %u\n", + parms->div_ref, parms->loopc, parms->div_out); +} + +/* + * LS7A1000, LS7A2000 and ls2k2000's pixel pll setting register is same, + * we take this as default, create a new instance if a different model is + * introduced. + */ +static const struct lsdc_pixpll_funcs __pixpll_default_funcs = { + .setup = lsdc_pixel_pll_setup, + .compute = lsdc_pixel_pll_compute, + .update = lsdc_pixpll_update, + .get_rate = lsdc_pixpll_get_freq, + .print = lsdc_pixpll_print, +}; + +/* pixel pll initialization */ + +int lsdc_pixpll_init(struct lsdc_pixpll * const this, + struct drm_device *ddev, + unsigned int index) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_desc *descp = ldev->descp; + const struct loongson_gfx_desc *gfx = to_loongson_gfx(descp); + + this->ddev = ddev; + this->reg_size = 8; + this->reg_base = gfx->conf_reg_base + gfx->pixpll[index].reg_offset; + this->funcs = &__pixpll_default_funcs; + + return this->funcs->setup(this); +} diff --git a/drivers/gpu/drm/loongson/lsdc_pixpll.h b/drivers/gpu/drm/loongson/lsdc_pixpll.h new file mode 100644 index 000000000000..ec3486d90ab6 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_pixpll.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_PIXPLL_H__ +#define __LSDC_PIXPLL_H__ + +#include <drm/drm_device.h> + +/* + * Loongson Pixel PLL hardware structure + * + * refclk: reference frequency, 100 MHz from external oscillator + * outclk: output frequency desired. + * + * + * L1 Fref Fvco L2 + * refclk +-----------+ +------------------+ +---------+ outclk + * ---+---> | Prescaler | ---> | Clock Multiplier | ---> | divider | --------> + * | +-----------+ +------------------+ +---------+ ^ + * | ^ ^ ^ | + * | | | | | + * | | | | | + * | div_ref loopc div_out | + * | | + * +---- bypass (bypass above software configurable clock if set) ----+ + * + * outclk = refclk / div_ref * loopc / div_out; + * + * sel_out: PLL clock output selector(enable). + * + * If sel_out == 1, then enable output clock (turn On); + * If sel_out == 0, then disable output clock (turn Off); + * + * PLL working requirements: + * + * 1) 20 MHz <= refclk / div_ref <= 40Mhz + * 2) 1.2 GHz <= refclk /div_out * loopc <= 3.2 Ghz + */ + +struct lsdc_pixpll_parms { + unsigned int ref_clock; + unsigned int div_ref; + unsigned int loopc; + unsigned int div_out; +}; + +struct lsdc_pixpll; + +struct lsdc_pixpll_funcs { + int (*setup)(struct lsdc_pixpll * const this); + + int (*compute)(struct lsdc_pixpll * const this, + unsigned int clock, + struct lsdc_pixpll_parms *pout); + + int (*update)(struct lsdc_pixpll * const this, + struct lsdc_pixpll_parms const *pin); + + unsigned int (*get_rate)(struct lsdc_pixpll * const this); + + void (*print)(struct lsdc_pixpll * const this, + struct drm_printer *printer); +}; + +struct lsdc_pixpll { + const struct lsdc_pixpll_funcs *funcs; + + struct drm_device *ddev; + + /* PLL register offset */ + u32 reg_base; + /* PLL register size in bytes */ + u32 reg_size; + + void __iomem *mmio; + + struct lsdc_pixpll_parms *priv; +}; + +int lsdc_pixpll_init(struct lsdc_pixpll * const this, + struct drm_device *ddev, + unsigned int index); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_plane.c b/drivers/gpu/drm/loongson/lsdc_plane.c new file mode 100644 index 000000000000..0d5094633222 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_plane.c @@ -0,0 +1,793 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/delay.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_plane_helper.h> + +#include "lsdc_drv.h" +#include "lsdc_regs.h" +#include "lsdc_ttm.h" + +static const u32 lsdc_primary_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static const u32 lsdc_cursor_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +static const u64 lsdc_fb_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static unsigned int lsdc_get_fb_offset(struct drm_framebuffer *fb, + struct drm_plane_state *state) +{ + unsigned int offset = fb->offsets[0]; + + offset += fb->format->cpp[0] * (state->src_x >> 16); + offset += fb->pitches[0] * (state->src_y >> 16); + + return offset; +} + +static u64 lsdc_fb_base_addr(struct drm_framebuffer *fb) +{ + struct lsdc_device *ldev = to_lsdc(fb->dev); + struct lsdc_bo *lbo = gem_to_lsdc_bo(fb->obj[0]); + + return lsdc_bo_gpu_offset(lbo) + ldev->vram_base; +} + +static int lsdc_primary_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc *crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state; + + if (!crtc) + return 0; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + return drm_atomic_helper_check_plane_state(new_plane_state, + new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, true); +} + +static void lsdc_primary_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_primary *primary = to_lsdc_primary(plane); + const struct lsdc_primary_plane_ops *ops = primary->ops; + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_framebuffer *old_fb = old_plane_state->fb; + u64 fb_addr = lsdc_fb_base_addr(new_fb); + + fb_addr += lsdc_get_fb_offset(new_fb, new_plane_state); + + ops->update_fb_addr(primary, fb_addr); + ops->update_fb_stride(primary, new_fb->pitches[0]); + + if (!old_fb || old_fb->format != new_fb->format) + ops->update_fb_format(primary, new_fb->format); +} + +static void lsdc_primary_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + /* + * Do nothing, just prevent call into atomic_update(). + * Writing the format as LSDC_PF_NONE can disable the primary, + * But it seems not necessary... + */ + drm_dbg(plane->dev, "%s disabled\n", plane->name); +} + +static int lsdc_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *fb = new_state->fb; + struct lsdc_bo *lbo; + u64 gpu_vaddr; + int ret; + + if (!fb) + return 0; + + lbo = gem_to_lsdc_bo(fb->obj[0]); + + ret = lsdc_bo_reserve(lbo); + if (unlikely(ret)) { + drm_err(plane->dev, "bo %p reserve failed\n", lbo); + return ret; + } + + ret = lsdc_bo_pin(lbo, LSDC_GEM_DOMAIN_VRAM, &gpu_vaddr); + + lsdc_bo_unreserve(lbo); + + if (unlikely(ret)) { + drm_err(plane->dev, "bo %p pin failed\n", lbo); + return ret; + } + + lsdc_bo_ref(lbo); + + if (plane->type != DRM_PLANE_TYPE_CURSOR) + drm_dbg(plane->dev, + "%s[%p] pin at 0x%llx, bo size: %zu\n", + plane->name, lbo, gpu_vaddr, lsdc_bo_size(lbo)); + + return drm_gem_plane_helper_prepare_fb(plane, new_state); +} + +static void lsdc_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_framebuffer *fb = old_state->fb; + struct lsdc_bo *lbo; + int ret; + + if (!fb) + return; + + lbo = gem_to_lsdc_bo(fb->obj[0]); + + ret = lsdc_bo_reserve(lbo); + if (unlikely(ret)) { + drm_err(plane->dev, "%p reserve failed\n", lbo); + return; + } + + lsdc_bo_unpin(lbo); + + lsdc_bo_unreserve(lbo); + + lsdc_bo_unref(lbo); + + if (plane->type != DRM_PLANE_TYPE_CURSOR) + drm_dbg(plane->dev, "%s unpin\n", plane->name); +} + +static const struct drm_plane_helper_funcs lsdc_primary_helper_funcs = { + .prepare_fb = lsdc_plane_prepare_fb, + .cleanup_fb = lsdc_plane_cleanup_fb, + .atomic_check = lsdc_primary_atomic_check, + .atomic_update = lsdc_primary_atomic_update, + .atomic_disable = lsdc_primary_atomic_disable, +}; + +static int lsdc_cursor_plane_atomic_async_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state; + struct drm_crtc_state *crtc_state; + + new_state = drm_atomic_get_new_plane_state(state, plane); + + if (!plane->state || !plane->state->fb) { + drm_dbg(plane->dev, "%s: state is NULL\n", plane->name); + return -EINVAL; + } + + if (new_state->crtc_w != new_state->crtc_h) { + drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n", + new_state->crtc_w, new_state->crtc_h); + return -EINVAL; + } + + if (new_state->crtc_w != 64 && new_state->crtc_w != 32) { + drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n", + new_state->crtc_w, new_state->crtc_h); + return -EINVAL; + } + + crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc); + if (!crtc_state->active) + return -EINVAL; + + if (plane->state->crtc != new_state->crtc || + plane->state->src_w != new_state->src_w || + plane->state->src_h != new_state->src_h || + plane->state->crtc_w != new_state->crtc_w || + plane->state->crtc_h != new_state->crtc_h) + return -EINVAL; + + if (new_state->visible != plane->state->visible) + return -EINVAL; + + return drm_atomic_helper_check_plane_state(plane->state, + crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); +} + +static void lsdc_cursor_plane_atomic_async_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + const struct lsdc_cursor_plane_ops *ops = cursor->ops; + struct drm_framebuffer *old_fb = plane->state->fb; + struct drm_framebuffer *new_fb; + struct drm_plane_state *new_state; + + new_state = drm_atomic_get_new_plane_state(state, plane); + + new_fb = plane->state->fb; + + plane->state->crtc_x = new_state->crtc_x; + plane->state->crtc_y = new_state->crtc_y; + plane->state->crtc_h = new_state->crtc_h; + plane->state->crtc_w = new_state->crtc_w; + plane->state->src_x = new_state->src_x; + plane->state->src_y = new_state->src_y; + plane->state->src_h = new_state->src_h; + plane->state->src_w = new_state->src_w; + swap(plane->state->fb, new_state->fb); + + if (new_state->visible) { + enum lsdc_cursor_size cursor_size; + + switch (new_state->crtc_w) { + case 64: + cursor_size = CURSOR_SIZE_64X64; + break; + case 32: + cursor_size = CURSOR_SIZE_32X32; + break; + default: + cursor_size = CURSOR_SIZE_32X32; + break; + } + + ops->update_position(cursor, new_state->crtc_x, new_state->crtc_y); + + ops->update_cfg(cursor, cursor_size, CURSOR_FORMAT_ARGB8888); + + if (!old_fb || old_fb != new_fb) + ops->update_bo_addr(cursor, lsdc_fb_base_addr(new_fb)); + } +} + +/* ls7a1000 cursor plane helpers */ + +static int ls7a1000_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state; + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + + crtc = new_plane_state->crtc; + if (!crtc) { + drm_dbg(plane->dev, "%s is not bind to a crtc\n", plane->name); + return 0; + } + + if (new_plane_state->crtc_w != 32 || new_plane_state->crtc_h != 32) { + drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n", + new_plane_state->crtc_w, new_plane_state->crtc_h); + return -EINVAL; + } + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + return drm_atomic_helper_check_plane_state(new_plane_state, + new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); +} + +static void ls7a1000_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_framebuffer *old_fb = old_plane_state->fb; + const struct lsdc_cursor_plane_ops *ops = cursor->ops; + u64 addr = lsdc_fb_base_addr(new_fb); + + if (!new_plane_state->visible) + return; + + ops->update_position(cursor, new_plane_state->crtc_x, new_plane_state->crtc_y); + + if (!old_fb || old_fb != new_fb) + ops->update_bo_addr(cursor, addr); + + ops->update_cfg(cursor, CURSOR_SIZE_32X32, CURSOR_FORMAT_ARGB8888); +} + +static void ls7a1000_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + const struct lsdc_cursor_plane_ops *ops = cursor->ops; + + ops->update_cfg(cursor, CURSOR_SIZE_32X32, CURSOR_FORMAT_DISABLE); +} + +static const struct drm_plane_helper_funcs ls7a1000_cursor_plane_helper_funcs = { + .prepare_fb = lsdc_plane_prepare_fb, + .cleanup_fb = lsdc_plane_cleanup_fb, + .atomic_check = ls7a1000_cursor_plane_atomic_check, + .atomic_update = ls7a1000_cursor_plane_atomic_update, + .atomic_disable = ls7a1000_cursor_plane_atomic_disable, + .atomic_async_check = lsdc_cursor_plane_atomic_async_check, + .atomic_async_update = lsdc_cursor_plane_atomic_async_update, +}; + +/* ls7a2000 cursor plane helpers */ + +static int ls7a2000_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state; + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + + crtc = new_plane_state->crtc; + if (!crtc) { + drm_dbg(plane->dev, "%s is not bind to a crtc\n", plane->name); + return 0; + } + + if (new_plane_state->crtc_w != new_plane_state->crtc_h) { + drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n", + new_plane_state->crtc_w, new_plane_state->crtc_h); + return -EINVAL; + } + + if (new_plane_state->crtc_w != 64 && new_plane_state->crtc_w != 32) { + drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n", + new_plane_state->crtc_w, new_plane_state->crtc_h); + return -EINVAL; + } + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + return drm_atomic_helper_check_plane_state(new_plane_state, + new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); +} + +/* Update the format, size and location of the cursor */ + +static void ls7a2000_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_framebuffer *old_fb = old_plane_state->fb; + const struct lsdc_cursor_plane_ops *ops = cursor->ops; + enum lsdc_cursor_size cursor_size; + + if (!new_plane_state->visible) + return; + + ops->update_position(cursor, new_plane_state->crtc_x, new_plane_state->crtc_y); + + if (!old_fb || new_fb != old_fb) { + u64 addr = lsdc_fb_base_addr(new_fb); + + ops->update_bo_addr(cursor, addr); + } + + switch (new_plane_state->crtc_w) { + case 64: + cursor_size = CURSOR_SIZE_64X64; + break; + case 32: + cursor_size = CURSOR_SIZE_32X32; + break; + default: + cursor_size = CURSOR_SIZE_64X64; + break; + } + + ops->update_cfg(cursor, cursor_size, CURSOR_FORMAT_ARGB8888); +} + +static void ls7a2000_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + const struct lsdc_cursor_plane_ops *hw_ops = cursor->ops; + + hw_ops->update_cfg(cursor, CURSOR_SIZE_64X64, CURSOR_FORMAT_DISABLE); +} + +static const struct drm_plane_helper_funcs ls7a2000_cursor_plane_helper_funcs = { + .prepare_fb = lsdc_plane_prepare_fb, + .cleanup_fb = lsdc_plane_cleanup_fb, + .atomic_check = ls7a2000_cursor_plane_atomic_check, + .atomic_update = ls7a2000_cursor_plane_atomic_update, + .atomic_disable = ls7a2000_cursor_plane_atomic_disable, + .atomic_async_check = lsdc_cursor_plane_atomic_async_check, + .atomic_async_update = lsdc_cursor_plane_atomic_async_update, +}; + +static void lsdc_plane_atomic_print_state(struct drm_printer *p, + const struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + u64 addr; + + if (!fb) + return; + + addr = lsdc_fb_base_addr(fb); + + drm_printf(p, "\tdma addr=%llx\n", addr); +} + +static const struct drm_plane_funcs lsdc_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_print_state = lsdc_plane_atomic_print_state, +}; + +/* Primary plane 0 hardware related ops */ + +static void lsdc_primary0_update_fb_addr(struct lsdc_primary *primary, u64 addr) +{ + struct lsdc_device *ldev = primary->ldev; + u32 status; + u32 lo, hi; + + /* 40-bit width physical address bus */ + lo = addr & 0xFFFFFFFF; + hi = (addr >> 32) & 0xFF; + + status = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); + if (status & FB_REG_IN_USING) { + lsdc_wreg32(ldev, LSDC_CRTC0_FB1_ADDR_LO_REG, lo); + lsdc_wreg32(ldev, LSDC_CRTC0_FB1_ADDR_HI_REG, hi); + } else { + lsdc_wreg32(ldev, LSDC_CRTC0_FB0_ADDR_LO_REG, lo); + lsdc_wreg32(ldev, LSDC_CRTC0_FB0_ADDR_HI_REG, hi); + } +} + +static void lsdc_primary0_update_fb_stride(struct lsdc_primary *primary, u32 stride) +{ + struct lsdc_device *ldev = primary->ldev; + + lsdc_wreg32(ldev, LSDC_CRTC0_STRIDE_REG, stride); +} + +static void lsdc_primary0_update_fb_format(struct lsdc_primary *primary, + const struct drm_format_info *format) +{ + struct lsdc_device *ldev = primary->ldev; + u32 status; + + status = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); + + /* + * TODO: add RGB565 support, only support XRBG8888 at present + */ + status &= ~CFG_PIX_FMT_MASK; + status |= LSDC_PF_XRGB8888; + + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, status); +} + +/* Primary plane 1 hardware related ops */ + +static void lsdc_primary1_update_fb_addr(struct lsdc_primary *primary, u64 addr) +{ + struct lsdc_device *ldev = primary->ldev; + u32 status; + u32 lo, hi; + + /* 40-bit width physical address bus */ + lo = addr & 0xFFFFFFFF; + hi = (addr >> 32) & 0xFF; + + status = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); + if (status & FB_REG_IN_USING) { + lsdc_wreg32(ldev, LSDC_CRTC1_FB1_ADDR_LO_REG, lo); + lsdc_wreg32(ldev, LSDC_CRTC1_FB1_ADDR_HI_REG, hi); + } else { + lsdc_wreg32(ldev, LSDC_CRTC1_FB0_ADDR_LO_REG, lo); + lsdc_wreg32(ldev, LSDC_CRTC1_FB0_ADDR_HI_REG, hi); + } +} + +static void lsdc_primary1_update_fb_stride(struct lsdc_primary *primary, u32 stride) +{ + struct lsdc_device *ldev = primary->ldev; + + lsdc_wreg32(ldev, LSDC_CRTC1_STRIDE_REG, stride); +} + +static void lsdc_primary1_update_fb_format(struct lsdc_primary *primary, + const struct drm_format_info *format) +{ + struct lsdc_device *ldev = primary->ldev; + u32 status; + + status = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); + + /* + * TODO: add RGB565 support, only support XRBG8888 at present + */ + status &= ~CFG_PIX_FMT_MASK; + status |= LSDC_PF_XRGB8888; + + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, status); +} + +static const struct lsdc_primary_plane_ops lsdc_primary_plane_hw_ops[2] = { + { + .update_fb_addr = lsdc_primary0_update_fb_addr, + .update_fb_stride = lsdc_primary0_update_fb_stride, + .update_fb_format = lsdc_primary0_update_fb_format, + }, + { + .update_fb_addr = lsdc_primary1_update_fb_addr, + .update_fb_stride = lsdc_primary1_update_fb_stride, + .update_fb_format = lsdc_primary1_update_fb_format, + }, +}; + +/* + * Update location, format, enable and disable state of the cursor, + * For those who have two hardware cursor, let cursor 0 is attach to CRTC-0, + * cursor 1 is attach to CRTC-1. Compositing the primary plane and cursor + * plane is automatically done by hardware, the cursor is alway on the top of + * the primary plane. In other word, z-order is fixed in hardware and cannot + * be changed. For those old DC who has only one hardware cursor, we made it + * shared by the two screen, this works on extend screen mode. + */ + +/* cursor plane 0 (for pipe 0) related hardware ops */ + +static void lsdc_cursor0_update_bo_addr(struct lsdc_cursor *cursor, u64 addr) +{ + struct lsdc_device *ldev = cursor->ldev; + + /* 40-bit width physical address bus */ + lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_HI_REG, (addr >> 32) & 0xFF); + lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG, addr); +} + +static void lsdc_cursor0_update_position(struct lsdc_cursor *cursor, int x, int y) +{ + struct lsdc_device *ldev = cursor->ldev; + + if (x < 0) + x = 0; + + if (y < 0) + y = 0; + + lsdc_wreg32(ldev, LSDC_CURSOR0_POSITION_REG, (y << 16) | x); +} + +static void lsdc_cursor0_update_cfg(struct lsdc_cursor *cursor, + enum lsdc_cursor_size cursor_size, + enum lsdc_cursor_format fmt) +{ + struct lsdc_device *ldev = cursor->ldev; + u32 cfg; + + cfg = CURSOR_ON_CRTC0 << CURSOR_LOCATION_SHIFT | + cursor_size << CURSOR_SIZE_SHIFT | + fmt << CURSOR_FORMAT_SHIFT; + + lsdc_wreg32(ldev, LSDC_CURSOR0_CFG_REG, cfg); +} + +/* cursor plane 1 (for pipe 1) related hardware ops */ + +static void lsdc_cursor1_update_bo_addr(struct lsdc_cursor *cursor, u64 addr) +{ + struct lsdc_device *ldev = cursor->ldev; + + /* 40-bit width physical address bus */ + lsdc_wreg32(ldev, LSDC_CURSOR1_ADDR_HI_REG, (addr >> 32) & 0xFF); + lsdc_wreg32(ldev, LSDC_CURSOR1_ADDR_LO_REG, addr); +} + +static void lsdc_cursor1_update_position(struct lsdc_cursor *cursor, int x, int y) +{ + struct lsdc_device *ldev = cursor->ldev; + + if (x < 0) + x = 0; + + if (y < 0) + y = 0; + + lsdc_wreg32(ldev, LSDC_CURSOR1_POSITION_REG, (y << 16) | x); +} + +static void lsdc_cursor1_update_cfg(struct lsdc_cursor *cursor, + enum lsdc_cursor_size cursor_size, + enum lsdc_cursor_format fmt) +{ + struct lsdc_device *ldev = cursor->ldev; + u32 cfg; + + cfg = CURSOR_ON_CRTC1 << CURSOR_LOCATION_SHIFT | + cursor_size << CURSOR_SIZE_SHIFT | + fmt << CURSOR_FORMAT_SHIFT; + + lsdc_wreg32(ldev, LSDC_CURSOR1_CFG_REG, cfg); +} + +/* The hardware cursors become normal since ls7a2000/ls2k2000 */ + +static const struct lsdc_cursor_plane_ops ls7a2000_cursor_hw_ops[2] = { + { + .update_bo_addr = lsdc_cursor0_update_bo_addr, + .update_cfg = lsdc_cursor0_update_cfg, + .update_position = lsdc_cursor0_update_position, + }, + { + .update_bo_addr = lsdc_cursor1_update_bo_addr, + .update_cfg = lsdc_cursor1_update_cfg, + .update_position = lsdc_cursor1_update_position, + }, +}; + +/* Quirks for cursor 1, only for old loongson display controller */ + +static void lsdc_cursor1_update_bo_addr_quirk(struct lsdc_cursor *cursor, u64 addr) +{ + struct lsdc_device *ldev = cursor->ldev; + + /* 40-bit width physical address bus */ + lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_HI_REG, (addr >> 32) & 0xFF); + lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG, addr); +} + +static void lsdc_cursor1_update_position_quirk(struct lsdc_cursor *cursor, int x, int y) +{ + struct lsdc_device *ldev = cursor->ldev; + + if (x < 0) + x = 0; + + if (y < 0) + y = 0; + + lsdc_wreg32(ldev, LSDC_CURSOR0_POSITION_REG, (y << 16) | x); +} + +static void lsdc_cursor1_update_cfg_quirk(struct lsdc_cursor *cursor, + enum lsdc_cursor_size cursor_size, + enum lsdc_cursor_format fmt) +{ + struct lsdc_device *ldev = cursor->ldev; + u32 cfg; + + cfg = CURSOR_ON_CRTC1 << CURSOR_LOCATION_SHIFT | + cursor_size << CURSOR_SIZE_SHIFT | + fmt << CURSOR_FORMAT_SHIFT; + + lsdc_wreg32(ldev, LSDC_CURSOR0_CFG_REG, cfg); +} + +/* + * The unforgiving LS7A1000/LS2K1000 has only one hardware cursors plane + */ +static const struct lsdc_cursor_plane_ops ls7a1000_cursor_hw_ops[2] = { + { + .update_bo_addr = lsdc_cursor0_update_bo_addr, + .update_cfg = lsdc_cursor0_update_cfg, + .update_position = lsdc_cursor0_update_position, + }, + { + .update_bo_addr = lsdc_cursor1_update_bo_addr_quirk, + .update_cfg = lsdc_cursor1_update_cfg_quirk, + .update_position = lsdc_cursor1_update_position_quirk, + }, +}; + +int lsdc_primary_plane_init(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index) +{ + struct lsdc_primary *primary = to_lsdc_primary(plane); + int ret; + + ret = drm_universal_plane_init(ddev, plane, 1 << index, + &lsdc_plane_funcs, + lsdc_primary_formats, + ARRAY_SIZE(lsdc_primary_formats), + lsdc_fb_format_modifiers, + DRM_PLANE_TYPE_PRIMARY, + "ls-primary-plane-%u", index); + if (ret) + return ret; + + drm_plane_helper_add(plane, &lsdc_primary_helper_funcs); + + primary->ldev = to_lsdc(ddev); + primary->ops = &lsdc_primary_plane_hw_ops[index]; + + return 0; +} + +int ls7a1000_cursor_plane_init(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + int ret; + + ret = drm_universal_plane_init(ddev, plane, 1 << index, + &lsdc_plane_funcs, + lsdc_cursor_formats, + ARRAY_SIZE(lsdc_cursor_formats), + lsdc_fb_format_modifiers, + DRM_PLANE_TYPE_CURSOR, + "ls-cursor-plane-%u", index); + if (ret) + return ret; + + cursor->ldev = to_lsdc(ddev); + cursor->ops = &ls7a1000_cursor_hw_ops[index]; + + drm_plane_helper_add(plane, &ls7a1000_cursor_plane_helper_funcs); + + return 0; +} + +int ls7a2000_cursor_plane_init(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + int ret; + + ret = drm_universal_plane_init(ddev, plane, 1 << index, + &lsdc_plane_funcs, + lsdc_cursor_formats, + ARRAY_SIZE(lsdc_cursor_formats), + lsdc_fb_format_modifiers, + DRM_PLANE_TYPE_CURSOR, + "ls-cursor-plane-%u", index); + if (ret) + return ret; + + cursor->ldev = to_lsdc(ddev); + cursor->ops = &ls7a2000_cursor_hw_ops[index]; + + drm_plane_helper_add(plane, &ls7a2000_cursor_plane_helper_funcs); + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_probe.c b/drivers/gpu/drm/loongson/lsdc_probe.c new file mode 100644 index 000000000000..48ba69bb8a98 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_probe.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include "lsdc_drv.h" +#include "lsdc_probe.h" + +/* + * Processor ID (implementation) values for bits 15:8 of the PRID register. + */ +#define LOONGSON_CPU_IMP_MASK 0xff00 +#define LOONGSON_CPU_IMP_SHIFT 8 + +#define LOONGARCH_CPU_IMP_LS2K1000 0xa0 +#define LOONGARCH_CPU_IMP_LS2K2000 0xb0 +#define LOONGARCH_CPU_IMP_LS3A5000 0xc0 + +#define LOONGSON_CPU_MIPS_IMP_LS2K 0x61 /* Loongson 2K Mips series SoC */ + +/* + * Particular Revision values for bits 7:0 of the PRID register. + */ +#define LOONGSON_CPU_REV_MASK 0x00ff + +#define LOONGARCH_CPUCFG_PRID_REG 0x0 + +/* + * We can achieve fine-grained control with the information about the host. + */ + +unsigned int loongson_cpu_get_prid(u8 *imp, u8 *rev) +{ + unsigned int prid = 0; + +#if defined(__loongarch__) + __asm__ volatile("cpucfg %0, %1\n\t" + : "=&r"(prid) + : "r"(LOONGARCH_CPUCFG_PRID_REG) + ); +#endif + +#if defined(__mips__) + __asm__ volatile("mfc0\t%0, $15\n\t" + : "=r" (prid) + ); +#endif + + if (imp) + *imp = (prid & LOONGSON_CPU_IMP_MASK) >> LOONGSON_CPU_IMP_SHIFT; + + if (rev) + *rev = prid & LOONGSON_CPU_REV_MASK; + + return prid; +} diff --git a/drivers/gpu/drm/loongson/lsdc_probe.h b/drivers/gpu/drm/loongson/lsdc_probe.h new file mode 100644 index 000000000000..8bb6de2e3c64 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_probe.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_PROBE_H__ +#define __LSDC_PROBE_H__ + +/* Helpers for chip detection */ +unsigned int loongson_cpu_get_prid(u8 *impl, u8 *rev); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_regs.h b/drivers/gpu/drm/loongson/lsdc_regs.h new file mode 100644 index 000000000000..e8ea28689c63 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_regs.h @@ -0,0 +1,406 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_REGS_H__ +#define __LSDC_REGS_H__ + +#include <linux/bitops.h> +#include <linux/types.h> + +/* + * PIXEL PLL Reference clock + */ +#define LSDC_PLL_REF_CLK_KHZ 100000 + +/* + * Those PLL registers are relative to LSxxxxx_CFG_REG_BASE. xxxxx = 7A1000, + * 7A2000, 2K2000, 2K1000 etc. + */ + +/* LS7A1000 */ + +#define LS7A1000_PIXPLL0_REG 0x04B0 +#define LS7A1000_PIXPLL1_REG 0x04C0 + +/* The DC, GPU, Graphic Memory Controller share the single gfxpll */ +#define LS7A1000_PLL_GFX_REG 0x0490 + +#define LS7A1000_CONF_REG_BASE 0x10010000 + +/* LS7A2000 */ + +#define LS7A2000_PIXPLL0_REG 0x04B0 +#define LS7A2000_PIXPLL1_REG 0x04C0 + +/* The DC, GPU, Graphic Memory Controller share the single gfxpll */ +#define LS7A2000_PLL_GFX_REG 0x0490 + +#define LS7A2000_CONF_REG_BASE 0x10010000 + +/* For LSDC_CRTCx_CFG_REG */ +#define CFG_PIX_FMT_MASK GENMASK(2, 0) + +enum lsdc_pixel_format { + LSDC_PF_NONE = 0, + LSDC_PF_XRGB444 = 1, /* [12 bits] */ + LSDC_PF_XRGB555 = 2, /* [15 bits] */ + LSDC_PF_XRGB565 = 3, /* RGB [16 bits] */ + LSDC_PF_XRGB8888 = 4, /* XRGB [32 bits] */ +}; + +/* + * Each crtc has two set fb address registers usable, FB_REG_IN_USING bit of + * LSDC_CRTCx_CFG_REG indicate which fb address register is in using by the + * CRTC currently. CFG_PAGE_FLIP is used to trigger the switch, the switching + * will be finished at the very next vblank. Trigger it again if you want to + * switch back. + * + * If FB0_ADDR_REG is in using, we write the address to FB0_ADDR_REG, + * if FB1_ADDR_REG is in using, we write the address to FB1_ADDR_REG. + */ +#define CFG_PAGE_FLIP BIT(7) +#define CFG_OUTPUT_ENABLE BIT(8) +#define CFG_HW_CLONE BIT(9) +/* Indicate witch fb addr reg is in using, currently. read only */ +#define FB_REG_IN_USING BIT(11) +#define CFG_GAMMA_EN BIT(12) + +/* The DC get soft reset if this bit changed from "1" to "0", active low */ +#define CFG_RESET_N BIT(20) +/* If this bit is set, it say that the CRTC stop working anymore, anchored. */ +#define CRTC_ANCHORED BIT(24) + +/* + * The DMA step of the DC in LS7A2000/LS2K2000 is configurable, + * setting those bits on ls7a1000 platform make no effect. + */ +#define CFG_DMA_STEP_MASK GENMASK(17, 16) +#define CFG_DMA_STEP_SHIFT 16 +enum lsdc_dma_steps { + LSDC_DMA_STEP_256_BYTES = 0, + LSDC_DMA_STEP_128_BYTES = 1, + LSDC_DMA_STEP_64_BYTES = 2, + LSDC_DMA_STEP_32_BYTES = 3, +}; + +#define CFG_VALID_BITS_MASK GENMASK(20, 0) + +/* For LSDC_CRTCx_HSYNC_REG */ +#define HSYNC_INV BIT(31) +#define HSYNC_EN BIT(30) +#define HSYNC_END_MASK GENMASK(28, 16) +#define HSYNC_END_SHIFT 16 +#define HSYNC_START_MASK GENMASK(12, 0) +#define HSYNC_START_SHIFT 0 + +/* For LSDC_CRTCx_VSYNC_REG */ +#define VSYNC_INV BIT(31) +#define VSYNC_EN BIT(30) +#define VSYNC_END_MASK GENMASK(27, 16) +#define VSYNC_END_SHIFT 16 +#define VSYNC_START_MASK GENMASK(11, 0) +#define VSYNC_START_SHIFT 0 + +/*********** CRTC0 ***********/ +#define LSDC_CRTC0_CFG_REG 0x1240 +#define LSDC_CRTC0_FB0_ADDR_LO_REG 0x1260 +#define LSDC_CRTC0_FB0_ADDR_HI_REG 0x15A0 +#define LSDC_CRTC0_STRIDE_REG 0x1280 +#define LSDC_CRTC0_FB_ORIGIN_REG 0x1300 +#define LSDC_CRTC0_HDISPLAY_REG 0x1400 +#define LSDC_CRTC0_HSYNC_REG 0x1420 +#define LSDC_CRTC0_VDISPLAY_REG 0x1480 +#define LSDC_CRTC0_VSYNC_REG 0x14A0 +#define LSDC_CRTC0_GAMMA_INDEX_REG 0x14E0 +#define LSDC_CRTC0_GAMMA_DATA_REG 0x1500 +#define LSDC_CRTC0_FB1_ADDR_LO_REG 0x1580 +#define LSDC_CRTC0_FB1_ADDR_HI_REG 0x15C0 + +/*********** CRTC1 ***********/ +#define LSDC_CRTC1_CFG_REG 0x1250 +#define LSDC_CRTC1_FB0_ADDR_LO_REG 0x1270 +#define LSDC_CRTC1_FB0_ADDR_HI_REG 0x15B0 +#define LSDC_CRTC1_STRIDE_REG 0x1290 +#define LSDC_CRTC1_FB_ORIGIN_REG 0x1310 +#define LSDC_CRTC1_HDISPLAY_REG 0x1410 +#define LSDC_CRTC1_HSYNC_REG 0x1430 +#define LSDC_CRTC1_VDISPLAY_REG 0x1490 +#define LSDC_CRTC1_VSYNC_REG 0x14B0 +#define LSDC_CRTC1_GAMMA_INDEX_REG 0x14F0 +#define LSDC_CRTC1_GAMMA_DATA_REG 0x1510 +#define LSDC_CRTC1_FB1_ADDR_LO_REG 0x1590 +#define LSDC_CRTC1_FB1_ADDR_HI_REG 0x15D0 + +/* For LSDC_CRTCx_DVO_CONF_REG */ +#define PHY_CLOCK_POL BIT(9) +#define PHY_CLOCK_EN BIT(8) +#define PHY_DE_POL BIT(1) +#define PHY_DATA_EN BIT(0) + +/*********** DVO0 ***********/ +#define LSDC_CRTC0_DVO_CONF_REG 0x13C0 + +/*********** DVO1 ***********/ +#define LSDC_CRTC1_DVO_CONF_REG 0x13D0 + +/* + * All of the DC variants has the hardware which record the scan position + * of the CRTC, [31:16] : current X position, [15:0] : current Y position + */ +#define LSDC_CRTC0_SCAN_POS_REG 0x14C0 +#define LSDC_CRTC1_SCAN_POS_REG 0x14D0 + +/* + * LS7A2000 has Sync Deviation register. + */ +#define SYNC_DEVIATION_EN BIT(31) +#define SYNC_DEVIATION_NUM GENMASK(12, 0) +#define LSDC_CRTC0_SYNC_DEVIATION_REG 0x1B80 +#define LSDC_CRTC1_SYNC_DEVIATION_REG 0x1B90 + +/* + * In gross, LSDC_CRTC1_XXX_REG - LSDC_CRTC0_XXX_REG = 0x10, but not all of + * the registers obey this rule, LSDC_CURSORx_XXX_REG just don't honor this. + * This is the root cause we can't untangle the code by manpulating offset + * of the register access simply. Our hardware engineers are lack experiance + * when they design this... + */ +#define CRTC_PIPE_OFFSET 0x10 + +/* + * There is only one hardware cursor unit in LS7A1000 and LS2K1000, let + * CFG_HW_CLONE_EN bit be "1" could eliminate this embarrassment, we made + * it on custom clone mode application. While LS7A2000 has two hardware + * cursor unit which is good enough. + */ +#define CURSOR_FORMAT_MASK GENMASK(1, 0) +#define CURSOR_FORMAT_SHIFT 0 +enum lsdc_cursor_format { + CURSOR_FORMAT_DISABLE = 0, + CURSOR_FORMAT_MONOCHROME = 1, /* masked */ + CURSOR_FORMAT_ARGB8888 = 2, /* A8R8G8B8 */ +}; + +/* + * LS7A1000 and LS2K1000 only support 32x32, LS2K2000 and LS7A2000 support + * 64x64, but it seems that setting this bit make no harms on LS7A1000, it + * just don't take effects. + */ +#define CURSOR_SIZE_SHIFT 2 +enum lsdc_cursor_size { + CURSOR_SIZE_32X32 = 0, + CURSOR_SIZE_64X64 = 1, +}; + +#define CURSOR_LOCATION_SHIFT 4 +enum lsdc_cursor_location { + CURSOR_ON_CRTC0 = 0, + CURSOR_ON_CRTC1 = 1, +}; + +#define LSDC_CURSOR0_CFG_REG 0x1520 +#define LSDC_CURSOR0_ADDR_LO_REG 0x1530 +#define LSDC_CURSOR0_ADDR_HI_REG 0x15e0 +#define LSDC_CURSOR0_POSITION_REG 0x1540 /* [31:16] Y, [15:0] X */ +#define LSDC_CURSOR0_BG_COLOR_REG 0x1550 /* background color */ +#define LSDC_CURSOR0_FG_COLOR_REG 0x1560 /* foreground color */ + +#define LSDC_CURSOR1_CFG_REG 0x1670 +#define LSDC_CURSOR1_ADDR_LO_REG 0x1680 +#define LSDC_CURSOR1_ADDR_HI_REG 0x16e0 +#define LSDC_CURSOR1_POSITION_REG 0x1690 /* [31:16] Y, [15:0] X */ +#define LSDC_CURSOR1_BG_COLOR_REG 0x16A0 /* background color */ +#define LSDC_CURSOR1_FG_COLOR_REG 0x16B0 /* foreground color */ + +/* + * DC Interrupt Control Register, 32bit, Address Offset: 1570 + * + * Bits 15:0 inidicate the interrupt status + * Bits 31:16 control enable interrupts corresponding to bit 15:0 or not + * Write 1 to enable, write 0 to disable + * + * RF: Read Finished + * IDBU: Internal Data Buffer Underflow + * IDBFU: Internal Data Buffer Fatal Underflow + * CBRF: Cursor Buffer Read Finished Flag, no use. + * FBRF0: CRTC-0 reading from its framebuffer finished. + * FBRF1: CRTC-1 reading from its framebuffer finished. + * + * +-------+--------------------------+-------+--------+--------+-------+ + * | 31:27 | 26:16 | 15:11 | 10 | 9 | 8 | + * +-------+--------------------------+-------+--------+--------+-------+ + * | N/A | Interrupt Enable Control | N/A | IDBFU0 | IDBFU1 | IDBU0 | + * +-------+--------------------------+-------+--------+--------+-------+ + * + * +-------+-------+-------+------+--------+--------+--------+--------+ + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * +-------+-------+-------+------+--------+--------+--------+--------+ + * | IDBU1 | FBRF0 | FBRF1 | CRRF | HSYNC0 | VSYNC0 | HSYNC1 | VSYNC1 | + * +-------+-------+-------+------+--------+--------+--------+--------+ + * + * unfortunately, CRTC0's interrupt is mess with CRTC1's interrupt in one + * register again. + */ + +#define LSDC_INT_REG 0x1570 + +#define INT_CRTC0_VSYNC BIT(2) +#define INT_CRTC0_HSYNC BIT(3) +#define INT_CRTC0_RF BIT(6) +#define INT_CRTC0_IDBU BIT(8) +#define INT_CRTC0_IDBFU BIT(10) + +#define INT_CRTC1_VSYNC BIT(0) +#define INT_CRTC1_HSYNC BIT(1) +#define INT_CRTC1_RF BIT(5) +#define INT_CRTC1_IDBU BIT(7) +#define INT_CRTC1_IDBFU BIT(9) + +#define INT_CRTC0_VSYNC_EN BIT(18) +#define INT_CRTC0_HSYNC_EN BIT(19) +#define INT_CRTC0_RF_EN BIT(22) +#define INT_CRTC0_IDBU_EN BIT(24) +#define INT_CRTC0_IDBFU_EN BIT(26) + +#define INT_CRTC1_VSYNC_EN BIT(16) +#define INT_CRTC1_HSYNC_EN BIT(17) +#define INT_CRTC1_RF_EN BIT(21) +#define INT_CRTC1_IDBU_EN BIT(23) +#define INT_CRTC1_IDBFU_EN BIT(25) + +#define INT_STATUS_MASK GENMASK(15, 0) + +/* + * LS7A1000/LS7A2000 have 4 gpios which are used to emulated I2C. + * They are under control of the LS7A_DC_GPIO_DAT_REG and LS7A_DC_GPIO_DIR_REG + * register, Those GPIOs has no relationship whth the GPIO hardware on the + * bridge chip itself. Those offsets are relative to DC register base address + * + * LS2k1000 don't have those registers, they use hardware i2c or general GPIO + * emulated i2c from linux i2c subsystem. + * + * GPIO data register, address offset: 0x1650 + * +---------------+-----------+-----------+ + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * +---------------+-----------+-----------+ + * | | DVO1 | DVO0 | + * + N/A +-----------+-----------+ + * | | SCL | SDA | SCL | SDA | + * +---------------+-----------+-----------+ + */ +#define LS7A_DC_GPIO_DAT_REG 0x1650 + +/* + * GPIO Input/Output direction control register, address offset: 0x1660 + */ +#define LS7A_DC_GPIO_DIR_REG 0x1660 + +/* + * LS7A2000 has two built-in HDMI Encoder and one VGA encoder + */ + +/* + * Number of continuous packets may be present + * in HDMI hblank and vblank zone, should >= 48 + */ +#define LSDC_HDMI0_ZONE_REG 0x1700 +#define LSDC_HDMI1_ZONE_REG 0x1710 + +#define HDMI_H_ZONE_IDLE_SHIFT 0 +#define HDMI_V_ZONE_IDLE_SHIFT 16 + +/* HDMI Iterface Control Reg */ +#define HDMI_INTERFACE_EN BIT(0) +#define HDMI_PACKET_EN BIT(1) +#define HDMI_AUDIO_EN BIT(2) +/* + * Preamble: + * Immediately preceding each video data period or data island period is the + * preamble. This is a sequence of eight identical control characters that + * indicate whether the upcoming data period is a video data period or is a + * data island. The values of CTL0, CTL1, CTL2, and CTL3 indicate the type of + * data period that follows. + */ +#define HDMI_VIDEO_PREAMBLE_MASK GENMASK(7, 4) +#define HDMI_VIDEO_PREAMBLE_SHIFT 4 +/* 1: hw i2c, 0: gpio emu i2c, shouldn't put in LSDC_HDMIx_INTF_CTRL_REG */ +#define HW_I2C_EN BIT(8) +#define HDMI_CTL_PERIOD_MODE BIT(9) +#define LSDC_HDMI0_INTF_CTRL_REG 0x1720 +#define LSDC_HDMI1_INTF_CTRL_REG 0x1730 + +#define HDMI_PHY_EN BIT(0) +#define HDMI_PHY_RESET_N BIT(1) +#define HDMI_PHY_TERM_L_EN BIT(8) +#define HDMI_PHY_TERM_H_EN BIT(9) +#define HDMI_PHY_TERM_DET_EN BIT(10) +#define HDMI_PHY_TERM_STATUS BIT(11) +#define LSDC_HDMI0_PHY_CTRL_REG 0x1800 +#define LSDC_HDMI1_PHY_CTRL_REG 0x1810 + +/* High level duration need > 1us */ +#define HDMI_PLL_ENABLE BIT(0) +#define HDMI_PLL_LOCKED BIT(16) +/* Bypass the software configured values, using default source from somewhere */ +#define HDMI_PLL_BYPASS BIT(17) + +#define HDMI_PLL_IDF_SHIFT 1 +#define HDMI_PLL_IDF_MASK GENMASK(5, 1) +#define HDMI_PLL_LF_SHIFT 6 +#define HDMI_PLL_LF_MASK GENMASK(12, 6) +#define HDMI_PLL_ODF_SHIFT 13 +#define HDMI_PLL_ODF_MASK GENMASK(15, 13) +#define LSDC_HDMI0_PHY_PLL_REG 0x1820 +#define LSDC_HDMI1_PHY_PLL_REG 0x1830 + +/* LS7A2000/LS2K2000 has hpd status reg, while the two hdmi's status + * located at the one register again. + */ +#define LSDC_HDMI_HPD_STATUS_REG 0x1BA0 +#define HDMI0_HPD_FLAG BIT(0) +#define HDMI1_HPD_FLAG BIT(1) + +#define LSDC_HDMI0_PHY_CAL_REG 0x18C0 +#define LSDC_HDMI1_PHY_CAL_REG 0x18D0 + +/* AVI InfoFrame */ +#define LSDC_HDMI0_AVI_CONTENT0 0x18E0 +#define LSDC_HDMI1_AVI_CONTENT0 0x18D0 +#define LSDC_HDMI0_AVI_CONTENT1 0x1900 +#define LSDC_HDMI1_AVI_CONTENT1 0x1910 +#define LSDC_HDMI0_AVI_CONTENT2 0x1920 +#define LSDC_HDMI1_AVI_CONTENT2 0x1930 +#define LSDC_HDMI0_AVI_CONTENT3 0x1940 +#define LSDC_HDMI1_AVI_CONTENT3 0x1950 + +/* 1: enable avi infoframe packet, 0: disable avi infoframe packet */ +#define AVI_PKT_ENABLE BIT(0) +/* 1: send one every two frame, 0: send one each frame */ +#define AVI_PKT_SEND_FREQ BIT(1) +/* + * 1: write 1 to flush avi reg content0 ~ content3 to the packet to be send, + * The hardware will clear this bit automatically. + */ +#define AVI_PKT_UPDATE BIT(2) + +#define LSDC_HDMI0_AVI_INFO_CRTL_REG 0x1960 +#define LSDC_HDMI1_AVI_INFO_CRTL_REG 0x1970 + +/* + * LS7A2000 has the hardware which count the number of vblank generated + */ +#define LSDC_CRTC0_VSYNC_COUNTER_REG 0x1A00 +#define LSDC_CRTC1_VSYNC_COUNTER_REG 0x1A10 + +/* + * LS7A2000 has the audio hardware associate with the HDMI encoder. + */ +#define LSDC_HDMI0_AUDIO_PLL_LO_REG 0x1A20 +#define LSDC_HDMI1_AUDIO_PLL_LO_REG 0x1A30 + +#define LSDC_HDMI0_AUDIO_PLL_HI_REG 0x1A40 +#define LSDC_HDMI1_AUDIO_PLL_HI_REG 0x1A50 + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_ttm.c b/drivers/gpu/drm/loongson/lsdc_ttm.c new file mode 100644 index 000000000000..bb0c8fd43a75 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_ttm.c @@ -0,0 +1,591 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_gem.h> +#include <drm/drm_managed.h> +#include <drm/drm_prime.h> + +#include "lsdc_drv.h" +#include "lsdc_ttm.h" + +const char *lsdc_mem_type_to_str(uint32_t mem_type) +{ + switch (mem_type) { + case TTM_PL_VRAM: + return "VRAM"; + case TTM_PL_TT: + return "GTT"; + case TTM_PL_SYSTEM: + return "SYSTEM"; + default: + break; + } + + return "Unknown"; +} + +const char *lsdc_domain_to_str(u32 domain) +{ + switch (domain) { + case LSDC_GEM_DOMAIN_VRAM: + return "VRAM"; + case LSDC_GEM_DOMAIN_GTT: + return "GTT"; + case LSDC_GEM_DOMAIN_SYSTEM: + return "SYSTEM"; + default: + break; + } + + return "Unknown"; +} + +static void lsdc_bo_set_placement(struct lsdc_bo *lbo, u32 domain) +{ + u32 c = 0; + u32 pflags = 0; + u32 i; + + if (lbo->tbo.base.size <= PAGE_SIZE) + pflags |= TTM_PL_FLAG_TOPDOWN; + + lbo->placement.placement = lbo->placements; + lbo->placement.busy_placement = lbo->placements; + + if (domain & LSDC_GEM_DOMAIN_VRAM) { + lbo->placements[c].mem_type = TTM_PL_VRAM; + lbo->placements[c++].flags = pflags; + } + + if (domain & LSDC_GEM_DOMAIN_GTT) { + lbo->placements[c].mem_type = TTM_PL_TT; + lbo->placements[c++].flags = pflags; + } + + if (domain & LSDC_GEM_DOMAIN_SYSTEM) { + lbo->placements[c].mem_type = TTM_PL_SYSTEM; + lbo->placements[c++].flags = 0; + } + + if (!c) { + lbo->placements[c].mem_type = TTM_PL_SYSTEM; + lbo->placements[c++].flags = 0; + } + + lbo->placement.num_placement = c; + lbo->placement.num_busy_placement = c; + + for (i = 0; i < c; ++i) { + lbo->placements[i].fpfn = 0; + lbo->placements[i].lpfn = 0; + } +} + +static void lsdc_ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *tt) +{ + ttm_tt_fini(tt); + kfree(tt); +} + +static struct ttm_tt * +lsdc_ttm_tt_create(struct ttm_buffer_object *tbo, uint32_t page_flags) +{ + struct ttm_tt *tt; + int ret; + + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + if (!tt) + return NULL; + + ret = ttm_sg_tt_init(tt, tbo, page_flags, ttm_cached); + if (ret < 0) { + kfree(tt); + return NULL; + } + + return tt; +} + +static int lsdc_ttm_tt_populate(struct ttm_device *bdev, + struct ttm_tt *ttm, + struct ttm_operation_ctx *ctx) +{ + bool slave = !!(ttm->page_flags & TTM_TT_FLAG_EXTERNAL); + + if (slave && ttm->sg) { + drm_prime_sg_to_dma_addr_array(ttm->sg, + ttm->dma_address, + ttm->num_pages); + + return 0; + } + + return ttm_pool_alloc(&bdev->pool, ttm, ctx); +} + +static void lsdc_ttm_tt_unpopulate(struct ttm_device *bdev, + struct ttm_tt *ttm) +{ + bool slave = !!(ttm->page_flags & TTM_TT_FLAG_EXTERNAL); + + if (slave) + return; + + return ttm_pool_free(&bdev->pool, ttm); +} + +static void lsdc_bo_evict_flags(struct ttm_buffer_object *tbo, + struct ttm_placement *tplacement) +{ + struct ttm_resource *resource = tbo->resource; + struct lsdc_bo *lbo = to_lsdc_bo(tbo); + + switch (resource->mem_type) { + case TTM_PL_VRAM: + lsdc_bo_set_placement(lbo, LSDC_GEM_DOMAIN_GTT); + break; + case TTM_PL_TT: + default: + lsdc_bo_set_placement(lbo, LSDC_GEM_DOMAIN_SYSTEM); + break; + } + + *tplacement = lbo->placement; +} + +static int lsdc_bo_move(struct ttm_buffer_object *tbo, + bool evict, + struct ttm_operation_ctx *ctx, + struct ttm_resource *new_mem, + struct ttm_place *hop) +{ + struct drm_device *ddev = tbo->base.dev; + struct ttm_resource *old_mem = tbo->resource; + struct lsdc_bo *lbo = to_lsdc_bo(tbo); + int ret; + + if (unlikely(tbo->pin_count > 0)) { + drm_warn(ddev, "Can't move a pinned BO\n"); + return -EINVAL; + } + + ret = ttm_bo_wait_ctx(tbo, ctx); + if (ret) + return ret; + + if (!old_mem) { + drm_dbg(ddev, "bo[%p] move: NULL to %s, size: %zu\n", + lbo, lsdc_mem_type_to_str(new_mem->mem_type), + lsdc_bo_size(lbo)); + ttm_bo_move_null(tbo, new_mem); + return 0; + } + + if (old_mem->mem_type == TTM_PL_SYSTEM && !tbo->ttm) { + ttm_bo_move_null(tbo, new_mem); + drm_dbg(ddev, "bo[%p] move: SYSTEM to NULL, size: %zu\n", + lbo, lsdc_bo_size(lbo)); + return 0; + } + + if (old_mem->mem_type == TTM_PL_SYSTEM && + new_mem->mem_type == TTM_PL_TT) { + drm_dbg(ddev, "bo[%p] move: SYSTEM to GTT, size: %zu\n", + lbo, lsdc_bo_size(lbo)); + ttm_bo_move_null(tbo, new_mem); + return 0; + } + + if (old_mem->mem_type == TTM_PL_TT && + new_mem->mem_type == TTM_PL_SYSTEM) { + drm_dbg(ddev, "bo[%p] move: GTT to SYSTEM, size: %zu\n", + lbo, lsdc_bo_size(lbo)); + ttm_resource_free(tbo, &tbo->resource); + ttm_bo_assign_mem(tbo, new_mem); + return 0; + } + + drm_dbg(ddev, "bo[%p] move: %s to %s, size: %zu\n", + lbo, + lsdc_mem_type_to_str(old_mem->mem_type), + lsdc_mem_type_to_str(new_mem->mem_type), + lsdc_bo_size(lbo)); + + return ttm_bo_move_memcpy(tbo, ctx, new_mem); +} + +static int lsdc_bo_reserve_io_mem(struct ttm_device *bdev, + struct ttm_resource *mem) +{ + struct lsdc_device *ldev = tdev_to_ldev(bdev); + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + break; + case TTM_PL_TT: + break; + case TTM_PL_VRAM: + mem->bus.offset = (mem->start << PAGE_SHIFT) + ldev->vram_base; + mem->bus.is_iomem = true; + mem->bus.caching = ttm_write_combined; + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct ttm_device_funcs lsdc_bo_driver = { + .ttm_tt_create = lsdc_ttm_tt_create, + .ttm_tt_populate = lsdc_ttm_tt_populate, + .ttm_tt_unpopulate = lsdc_ttm_tt_unpopulate, + .ttm_tt_destroy = lsdc_ttm_tt_destroy, + .eviction_valuable = ttm_bo_eviction_valuable, + .evict_flags = lsdc_bo_evict_flags, + .move = lsdc_bo_move, + .io_mem_reserve = lsdc_bo_reserve_io_mem, +}; + +u64 lsdc_bo_gpu_offset(struct lsdc_bo *lbo) +{ + struct ttm_buffer_object *tbo = &lbo->tbo; + struct drm_device *ddev = tbo->base.dev; + struct ttm_resource *resource = tbo->resource; + + if (unlikely(!tbo->pin_count)) { + drm_err(ddev, "unpinned bo, gpu virtual address is invalid\n"); + return 0; + } + + if (unlikely(resource->mem_type == TTM_PL_SYSTEM)) + return 0; + + return resource->start << PAGE_SHIFT; +} + +size_t lsdc_bo_size(struct lsdc_bo *lbo) +{ + struct ttm_buffer_object *tbo = &lbo->tbo; + + return tbo->base.size; +} + +int lsdc_bo_reserve(struct lsdc_bo *lbo) +{ + return ttm_bo_reserve(&lbo->tbo, true, false, NULL); +} + +void lsdc_bo_unreserve(struct lsdc_bo *lbo) +{ + return ttm_bo_unreserve(&lbo->tbo); +} + +int lsdc_bo_pin(struct lsdc_bo *lbo, u32 domain, u64 *gpu_addr) +{ + struct ttm_operation_ctx ctx = { false, false }; + struct ttm_buffer_object *tbo = &lbo->tbo; + struct lsdc_device *ldev = tdev_to_ldev(tbo->bdev); + int ret; + + if (tbo->pin_count) + goto bo_pinned; + + if (lbo->sharing_count && domain == LSDC_GEM_DOMAIN_VRAM) + return -EINVAL; + + if (domain) + lsdc_bo_set_placement(lbo, domain); + + ret = ttm_bo_validate(tbo, &lbo->placement, &ctx); + if (unlikely(ret)) { + drm_err(&ldev->base, "%p validate failed: %d\n", lbo, ret); + return ret; + } + + if (domain == LSDC_GEM_DOMAIN_VRAM) + ldev->vram_pinned_size += lsdc_bo_size(lbo); + else if (domain == LSDC_GEM_DOMAIN_GTT) + ldev->gtt_pinned_size += lsdc_bo_size(lbo); + +bo_pinned: + ttm_bo_pin(tbo); + + if (gpu_addr) + *gpu_addr = lsdc_bo_gpu_offset(lbo); + + return 0; +} + +void lsdc_bo_unpin(struct lsdc_bo *lbo) +{ + struct ttm_buffer_object *tbo = &lbo->tbo; + struct lsdc_device *ldev = tdev_to_ldev(tbo->bdev); + + if (unlikely(!tbo->pin_count)) { + drm_dbg(&ldev->base, "%p unpin is not necessary\n", lbo); + return; + } + + ttm_bo_unpin(tbo); + + if (!tbo->pin_count) { + if (tbo->resource->mem_type == TTM_PL_VRAM) + ldev->vram_pinned_size -= lsdc_bo_size(lbo); + else if (tbo->resource->mem_type == TTM_PL_TT) + ldev->gtt_pinned_size -= lsdc_bo_size(lbo); + } +} + +void lsdc_bo_ref(struct lsdc_bo *lbo) +{ + struct ttm_buffer_object *tbo = &lbo->tbo; + + ttm_bo_get(tbo); +} + +void lsdc_bo_unref(struct lsdc_bo *lbo) +{ + struct ttm_buffer_object *tbo = &lbo->tbo; + + ttm_bo_put(tbo); +} + +int lsdc_bo_kmap(struct lsdc_bo *lbo) +{ + struct ttm_buffer_object *tbo = &lbo->tbo; + struct drm_gem_object *gem = &tbo->base; + struct drm_device *ddev = gem->dev; + long ret; + int err; + + ret = dma_resv_wait_timeout(gem->resv, DMA_RESV_USAGE_KERNEL, false, + MAX_SCHEDULE_TIMEOUT); + if (ret < 0) { + drm_warn(ddev, "wait fence timeout\n"); + return ret; + } + + if (lbo->kptr) + return 0; + + err = ttm_bo_kmap(tbo, 0, PFN_UP(lsdc_bo_size(lbo)), &lbo->kmap); + if (err) { + drm_err(ddev, "kmap %p failed: %d\n", lbo, err); + return err; + } + + lbo->kptr = ttm_kmap_obj_virtual(&lbo->kmap, &lbo->is_iomem); + + return 0; +} + +void lsdc_bo_kunmap(struct lsdc_bo *lbo) +{ + if (!lbo->kptr) + return; + + lbo->kptr = NULL; + ttm_bo_kunmap(&lbo->kmap); +} + +void lsdc_bo_clear(struct lsdc_bo *lbo) +{ + lsdc_bo_kmap(lbo); + + if (lbo->is_iomem) + memset_io((void __iomem *)lbo->kptr, 0, lbo->size); + else + memset(lbo->kptr, 0, lbo->size); + + lsdc_bo_kunmap(lbo); +} + +int lsdc_bo_evict_vram(struct drm_device *ddev) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + struct ttm_device *bdev = &ldev->bdev; + struct ttm_resource_manager *man; + + man = ttm_manager_type(bdev, TTM_PL_VRAM); + if (unlikely(!man)) + return 0; + + return ttm_resource_manager_evict_all(bdev, man); +} + +static void lsdc_bo_destroy(struct ttm_buffer_object *tbo) +{ + struct lsdc_device *ldev = tdev_to_ldev(tbo->bdev); + struct lsdc_bo *lbo = to_lsdc_bo(tbo); + + mutex_lock(&ldev->gem.mutex); + list_del_init(&lbo->list); + mutex_unlock(&ldev->gem.mutex); + + drm_gem_object_release(&tbo->base); + + kfree(lbo); +} + +struct lsdc_bo *lsdc_bo_create(struct drm_device *ddev, + u32 domain, + size_t size, + bool kernel, + struct sg_table *sg, + struct dma_resv *resv) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + struct ttm_device *bdev = &ldev->bdev; + struct ttm_buffer_object *tbo; + struct lsdc_bo *lbo; + enum ttm_bo_type bo_type; + int ret; + + lbo = kzalloc(sizeof(*lbo), GFP_KERNEL); + if (!lbo) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&lbo->list); + + lbo->initial_domain = domain & (LSDC_GEM_DOMAIN_VRAM | + LSDC_GEM_DOMAIN_GTT | + LSDC_GEM_DOMAIN_SYSTEM); + + tbo = &lbo->tbo; + + size = ALIGN(size, PAGE_SIZE); + + ret = drm_gem_object_init(ddev, &tbo->base, size); + if (ret) { + kfree(lbo); + return ERR_PTR(ret); + } + + tbo->bdev = bdev; + + if (kernel) + bo_type = ttm_bo_type_kernel; + else if (sg) + bo_type = ttm_bo_type_sg; + else + bo_type = ttm_bo_type_device; + + lsdc_bo_set_placement(lbo, domain); + lbo->size = size; + + ret = ttm_bo_init_validate(bdev, tbo, bo_type, &lbo->placement, 0, + false, sg, resv, lsdc_bo_destroy); + if (ret) { + kfree(lbo); + return ERR_PTR(ret); + } + + return lbo; +} + +struct lsdc_bo *lsdc_bo_create_kernel_pinned(struct drm_device *ddev, + u32 domain, + size_t size) +{ + struct lsdc_bo *lbo; + int ret; + + lbo = lsdc_bo_create(ddev, domain, size, true, NULL, NULL); + + ret = lsdc_bo_reserve(lbo); + if (unlikely(ret)) { + lsdc_bo_unref(lbo); + return ERR_PTR(ret); + } + + ret = lsdc_bo_pin(lbo, domain, NULL); + lsdc_bo_unreserve(lbo); + if (unlikely(ret)) { + lsdc_bo_unref(lbo); + return ERR_PTR(ret); + } + + return lbo; +} + +void lsdc_bo_free_kernel_pinned(struct lsdc_bo *lbo) +{ + int ret; + + ret = lsdc_bo_reserve(lbo); + if (unlikely(ret)) + return; + + lsdc_bo_unpin(lbo); + lsdc_bo_unreserve(lbo); + + lsdc_bo_unref(lbo); +} + +static void lsdc_ttm_fini(struct drm_device *ddev, void *data) +{ + struct lsdc_device *ldev = (struct lsdc_device *)data; + + ttm_range_man_fini(&ldev->bdev, TTM_PL_VRAM); + ttm_range_man_fini(&ldev->bdev, TTM_PL_TT); + + ttm_device_fini(&ldev->bdev); + + drm_dbg(ddev, "ttm finished\n"); +} + +int lsdc_ttm_init(struct lsdc_device *ldev) +{ + struct drm_device *ddev = &ldev->base; + unsigned long num_vram_pages; + unsigned long num_gtt_pages; + int ret; + + ret = ttm_device_init(&ldev->bdev, &lsdc_bo_driver, ddev->dev, + ddev->anon_inode->i_mapping, + ddev->vma_offset_manager, false, true); + if (ret) + return ret; + + num_vram_pages = ldev->vram_size >> PAGE_SHIFT; + + ret = ttm_range_man_init(&ldev->bdev, TTM_PL_VRAM, false, num_vram_pages); + if (unlikely(ret)) + return ret; + + drm_info(ddev, "VRAM: %lu pages ready\n", num_vram_pages); + + /* 512M is far enough for us now */ + ldev->gtt_size = 512 << 20; + + num_gtt_pages = ldev->gtt_size >> PAGE_SHIFT; + + ret = ttm_range_man_init(&ldev->bdev, TTM_PL_TT, true, num_gtt_pages); + if (unlikely(ret)) + return ret; + + drm_info(ddev, "GTT: %lu pages ready\n", num_gtt_pages); + + return drmm_add_action_or_reset(ddev, lsdc_ttm_fini, ldev); +} + +void lsdc_ttm_debugfs_init(struct lsdc_device *ldev) +{ + struct ttm_device *bdev = &ldev->bdev; + struct drm_device *ddev = &ldev->base; + struct drm_minor *minor = ddev->primary; + struct dentry *root = minor->debugfs_root; + struct ttm_resource_manager *vram_man; + struct ttm_resource_manager *gtt_man; + + vram_man = ttm_manager_type(bdev, TTM_PL_VRAM); + gtt_man = ttm_manager_type(bdev, TTM_PL_TT); + + ttm_resource_manager_create_debugfs(vram_man, root, "vram_mm"); + ttm_resource_manager_create_debugfs(gtt_man, root, "gtt_mm"); +} diff --git a/drivers/gpu/drm/loongson/lsdc_ttm.h b/drivers/gpu/drm/loongson/lsdc_ttm.h new file mode 100644 index 000000000000..843e1475064e --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_ttm.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LSDC_TTM_H__ +#define __LSDC_TTM_H__ + +#include <linux/container_of.h> +#include <linux/iosys-map.h> +#include <linux/list.h> + +#include <drm/drm_gem.h> +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_range_manager.h> +#include <drm/ttm/ttm_tt.h> + +#define LSDC_GEM_DOMAIN_SYSTEM 0x1 +#define LSDC_GEM_DOMAIN_GTT 0x2 +#define LSDC_GEM_DOMAIN_VRAM 0x4 + +struct lsdc_bo { + struct ttm_buffer_object tbo; + + /* Protected by gem.mutex */ + struct list_head list; + + struct iosys_map map; + + unsigned int vmap_count; + /* cross device driver sharing reference count */ + unsigned int sharing_count; + + struct ttm_bo_kmap_obj kmap; + void *kptr; + bool is_iomem; + + size_t size; + + u32 initial_domain; + + struct ttm_placement placement; + struct ttm_place placements[4]; +}; + +static inline struct ttm_buffer_object *to_ttm_bo(struct drm_gem_object *gem) +{ + return container_of(gem, struct ttm_buffer_object, base); +} + +static inline struct lsdc_bo *to_lsdc_bo(struct ttm_buffer_object *tbo) +{ + return container_of(tbo, struct lsdc_bo, tbo); +} + +static inline struct lsdc_bo *gem_to_lsdc_bo(struct drm_gem_object *gem) +{ + return container_of(gem, struct lsdc_bo, tbo.base); +} + +const char *lsdc_mem_type_to_str(uint32_t mem_type); +const char *lsdc_domain_to_str(u32 domain); + +struct lsdc_bo *lsdc_bo_create(struct drm_device *ddev, + u32 domain, + size_t size, + bool kernel, + struct sg_table *sg, + struct dma_resv *resv); + +struct lsdc_bo *lsdc_bo_create_kernel_pinned(struct drm_device *ddev, + u32 domain, + size_t size); + +void lsdc_bo_free_kernel_pinned(struct lsdc_bo *lbo); + +int lsdc_bo_reserve(struct lsdc_bo *lbo); +void lsdc_bo_unreserve(struct lsdc_bo *lbo); + +int lsdc_bo_pin(struct lsdc_bo *lbo, u32 domain, u64 *gpu_addr); +void lsdc_bo_unpin(struct lsdc_bo *lbo); + +void lsdc_bo_ref(struct lsdc_bo *lbo); +void lsdc_bo_unref(struct lsdc_bo *lbo); + +u64 lsdc_bo_gpu_offset(struct lsdc_bo *lbo); +size_t lsdc_bo_size(struct lsdc_bo *lbo); + +int lsdc_bo_kmap(struct lsdc_bo *lbo); +void lsdc_bo_kunmap(struct lsdc_bo *lbo); +void lsdc_bo_clear(struct lsdc_bo *lbo); + +int lsdc_bo_evict_vram(struct drm_device *ddev); + +int lsdc_ttm_init(struct lsdc_device *ldev); +void lsdc_ttm_debugfs_init(struct lsdc_device *ldev); + +#endif diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index a8cd86c06c14..7c31c54ab7fc 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -448,7 +448,7 @@ regulator_epod_off: } -static int mcde_remove(struct platform_device *pdev) +static void mcde_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); struct mcde *mcde = to_mcde(drm); @@ -457,8 +457,6 @@ static int mcde_remove(struct platform_device *pdev) clk_disable_unprepare(mcde->mcde_clk); regulator_disable(mcde->vana); regulator_disable(mcde->epod); - - return 0; } static const struct of_device_id mcde_of_match[] = { @@ -474,7 +472,7 @@ static struct platform_driver mcde_driver = { .of_match_table = of_match_ptr(mcde_of_match), }, .probe = mcde_probe, - .remove = mcde_remove, + .remove_new = mcde_remove, }; static struct platform_driver *const component_drivers[] = { diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index 9f9ac8699310..db82b38600b2 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -1208,14 +1208,12 @@ static int mcde_dsi_probe(struct platform_device *pdev) return component_add(dev, &mcde_dsi_component_ops); } -static int mcde_dsi_remove(struct platform_device *pdev) +static void mcde_dsi_remove(struct platform_device *pdev) { struct mcde_dsi *d = platform_get_drvdata(pdev); component_del(&pdev->dev, &mcde_dsi_component_ops); mipi_dsi_host_unregister(&d->dsi_host); - - return 0; } static const struct of_device_id mcde_dsi_of_match[] = { @@ -1231,5 +1229,5 @@ struct platform_driver mcde_dsi_driver = { .of_match_table = of_match_ptr(mcde_dsi_of_match), }, .probe = mcde_dsi_probe, - .remove = mcde_dsi_remove, + .remove_new = mcde_dsi_remove, }; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 6dcb4ba2466c..7fb65eb95c55 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -556,11 +556,8 @@ static const struct drm_driver mtk_drm_driver = { .dumb_create = mtk_drm_gem_dumb_create, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = mtk_drm_gem_prime_import, .gem_prime_import_sg_table = mtk_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .fops = &mtk_drm_fops, .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 747b639ea0c4..cb674966e9ac 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -516,11 +516,9 @@ static int meson_drv_probe(struct platform_device *pdev) return 0; }; -static int meson_drv_remove(struct platform_device *pdev) +static void meson_drv_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &meson_drv_master_ops); - - return 0; } static struct meson_drm_match_data meson_drm_gxbb_data = { @@ -560,7 +558,7 @@ static const struct dev_pm_ops meson_drv_pm_ops = { static struct platform_driver meson_drm_platform_driver = { .probe = meson_drv_probe, - .remove = meson_drv_remove, + .remove_new = meson_drv_remove, .shutdown = meson_drv_shutdown, .driver = { .name = "meson-drm", diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c index 3d046878ce6c..18a0f4f4638c 100644 --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -379,8 +379,8 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, mode->clock > 340000 ? 40 : 10); if (drm_mode_is_420_only(display, mode) || - (!is_hdmi2_sink && - drm_mode_is_420_also(display, mode))) + (!is_hdmi2_sink && drm_mode_is_420_also(display, mode)) || + dw_hdmi_bus_fmt_is_420(hdmi)) mode_is_420 = true; /* Enable clocks */ @@ -852,11 +852,9 @@ static int meson_dw_hdmi_probe(struct platform_device *pdev) return component_add(&pdev->dev, &meson_dw_hdmi_ops); } -static int meson_dw_hdmi_remove(struct platform_device *pdev) +static void meson_dw_hdmi_remove(struct platform_device *pdev) { component_del(&pdev->dev, &meson_dw_hdmi_ops); - - return 0; } static const struct dev_pm_ops meson_dw_hdmi_pm_ops = { @@ -879,7 +877,7 @@ MODULE_DEVICE_TABLE(of, meson_dw_hdmi_of_table); static struct platform_driver meson_dw_hdmi_platform_driver = { .probe = meson_dw_hdmi_probe, - .remove = meson_dw_hdmi_remove, + .remove_new = meson_dw_hdmi_remove, .driver = { .name = DRIVER_NAME, .of_match_table = meson_dw_hdmi_of_table, diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 976f0ab2006b..abddf37f0ea1 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -20,7 +20,7 @@ #include "mgag200_drv.h" -int mgag200_modeset = -1; +static int mgag200_modeset = -1; MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); module_param_named(modeset, mgag200_modeset, int, 0400); diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 891eff8433a9..2a0e3529598b 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -1086,10 +1086,7 @@ static const struct drm_driver msm_driver = { .postclose = msm_postclose, .dumb_create = msm_gem_dumb_create, .dumb_map_offset = msm_gem_dumb_map_offset, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = msm_gem_prime_import_sg_table, - .gem_prime_mmap = msm_gem_prime_mmap, #ifdef CONFIG_DEBUG_FS .debugfs_init = msm_debugfs_init, #endif diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index e13a8cbd61c9..44c9e06f2dff 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -282,7 +282,6 @@ unsigned long msm_gem_shrinker_shrink(struct drm_device *dev, unsigned long nr_t void msm_gem_shrinker_init(struct drm_device *dev); void msm_gem_shrinker_cleanup(struct drm_device *dev); -int msm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj); int msm_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map); void msm_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 20cfd86d2b32..635744bc4765 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -1234,6 +1234,10 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, uint32_t size, uint32 list_add_tail(&msm_obj->node, &priv->objects); mutex_unlock(&priv->obj_lock); + ret = drm_gem_create_mmap_offset(obj); + if (ret) + goto fail; + return obj; fail: @@ -1290,6 +1294,10 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, list_add_tail(&msm_obj->node, &priv->objects); mutex_unlock(&priv->obj_lock); + ret = drm_gem_create_mmap_offset(obj); + if (ret) + goto fail; + return obj; fail: diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c index c1d91863df05..5f68e31a3e4e 100644 --- a/drivers/gpu/drm/msm/msm_gem_prime.c +++ b/drivers/gpu/drm/msm/msm_gem_prime.c @@ -11,21 +11,6 @@ #include "msm_drv.h" #include "msm_gem.h" -int msm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) -{ - int ret; - - /* Ensure the mmap offset is initialized. We lazily initialize it, - * so if it has not been first mmap'd directly as a GEM object, the - * mmap offset will not be already initialized. - */ - ret = drm_gem_create_mmap_offset(obj); - if (ret) - return ret; - - return drm_gem_prime_mmap(obj, vma); -} - struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj) { struct msm_gem_object *msm_obj = to_msm_bo(obj); diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c index c9d8cbb21407..9f4212f54af7 100644 --- a/drivers/gpu/drm/mxsfb/lcdif_drv.c +++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c @@ -285,7 +285,7 @@ err_free: return ret; } -static int lcdif_remove(struct platform_device *pdev) +static void lcdif_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); @@ -293,8 +293,6 @@ static int lcdif_remove(struct platform_device *pdev) drm_atomic_helper_shutdown(drm); lcdif_unload(drm); drm_dev_put(drm); - - return 0; } static void lcdif_shutdown(struct platform_device *pdev) @@ -362,7 +360,7 @@ static const struct dev_pm_ops lcdif_pm_ops = { static struct platform_driver lcdif_platform_driver = { .probe = lcdif_probe, - .remove = lcdif_remove, + .remove_new = lcdif_remove, .shutdown = lcdif_shutdown, .driver = { .name = "imx-lcdif", diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 368b1fbd8305..625c1bfc4173 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -377,7 +377,7 @@ err_free: return ret; } -static int mxsfb_remove(struct platform_device *pdev) +static void mxsfb_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); @@ -385,8 +385,6 @@ static int mxsfb_remove(struct platform_device *pdev) drm_atomic_helper_shutdown(drm); mxsfb_unload(drm); drm_dev_put(drm); - - return 0; } static void mxsfb_shutdown(struct platform_device *pdev) @@ -418,7 +416,7 @@ static const struct dev_pm_ops mxsfb_pm_ops = { static struct platform_driver mxsfb_platform_driver = { .probe = mxsfb_probe, - .remove = mxsfb_remove, + .remove_new = mxsfb_remove, .shutdown = mxsfb_shutdown, .driver = { .name = "mxsfb", diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index 3bcc9c0f2019..7ed2516b6de0 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -611,6 +611,14 @@ static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane, writel(ctrl, mxsfb->base + LCDC_AS_CTRL); } +static void mxsfb_plane_overlay_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev); + + writel(0, mxsfb->base + LCDC_AS_CTRL); +} + static bool mxsfb_format_mod_supported(struct drm_plane *plane, uint32_t format, uint64_t modifier) @@ -626,6 +634,7 @@ static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = { static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = { .atomic_check = mxsfb_plane_atomic_check, .atomic_update = mxsfb_plane_overlay_atomic_update, + .atomic_disable = mxsfb_plane_overlay_atomic_disable, }; static const struct drm_plane_funcs mxsfb_plane_funcs = { diff --git a/drivers/gpu/drm/nouveau/dispnv50/curs507a.c b/drivers/gpu/drm/nouveau/dispnv50/curs507a.c index 78ee32da01c8..a95ee5dcc2e3 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/curs507a.c +++ b/drivers/gpu/drm/nouveau/dispnv50/curs507a.c @@ -29,6 +29,7 @@ #include <nvhw/class/cl507a.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_fourcc.h> bool curs507a_space(struct nv50_wndw *wndw) @@ -99,6 +100,7 @@ curs507a_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw, { struct nouveau_drm *drm = nouveau_drm(wndw->plane.dev); struct nv50_head *head = nv50_head(asyw->state.crtc); + struct drm_framebuffer *fb = asyw->state.fb; int ret; ret = drm_atomic_helper_check_plane_state(&asyw->state, &asyh->state, @@ -124,11 +126,30 @@ curs507a_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw, return -EINVAL; } + if (asyw->image.pitch[0] != asyw->image.w * fb->format->cpp[0]) { + NV_ATOMIC(drm, + "%s: invalid cursor image pitch: image must be packed (pitch = %d, width = %d)\n", + wndw->plane.name, asyw->image.pitch[0], asyw->image.w); + return -EINVAL; + } + ret = head->func->curs_layout(head, asyw, asyh); - if (ret) + if (ret) { + NV_ATOMIC(drm, + "%s: invalid cursor image size: unsupported size %dx%d\n", + wndw->plane.name, asyw->image.w, asyw->image.h); + return ret; + } + + ret = head->func->curs_format(head, asyw, asyh); + if (ret) { + NV_ATOMIC(drm, + "%s: invalid cursor image format 0x%X\n", + wndw->plane.name, fb->format->format); return ret; + } - return head->func->curs_format(head, asyw, asyh); + return 0; } static const u32 diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 38d3fad0d97a..d18e24b6f1eb 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1128,7 +1128,7 @@ nv50_mstc_mode_valid(struct drm_connector *connector, * MSTB's max possible PBN */ - return nv50_dp_mode_valid(connector, outp, mode, NULL); + return nv50_dp_mode_valid(outp, mode, NULL); } static int diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h b/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h index b67b9c1a6b4e..738899fcf30b 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h @@ -3,7 +3,7 @@ #define __NVKM_ENGINE_H__ #define nvkm_engine(p) container_of((p), struct nvkm_engine, subdev) #include <core/subdev.h> -struct nvkm_fifo_chan; +struct nvkm_chan; struct nvkm_fb_tile; extern const struct nvkm_subdev_func nvkm_engine; @@ -22,6 +22,7 @@ struct nvkm_engine_func { int (*init)(struct nvkm_engine *); int (*fini)(struct nvkm_engine *, bool suspend); int (*reset)(struct nvkm_engine *); + int (*nonstall)(struct nvkm_engine *); void (*intr)(struct nvkm_engine *); void (*tile)(struct nvkm_engine *, int region, struct nvkm_fb_tile *); bool (*chsw_load)(struct nvkm_engine *); @@ -32,8 +33,7 @@ struct nvkm_engine_func { } base; struct { - int (*cclass)(struct nvkm_fifo_chan *, - const struct nvkm_oclass *, + int (*cclass)(struct nvkm_chan *, const struct nvkm_oclass *, struct nvkm_object **); int (*sclass)(struct nvkm_oclass *, int index); } fifo; diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/os.h b/drivers/gpu/drm/nouveau/include/nvkm/core/os.h index 4486d9862849..3fd5c007a663 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/os.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/os.h @@ -49,9 +49,4 @@ nvkm_blob_dtor(struct nvkm_blob *blob) (p = container_of((h), typeof(*p), m), nvkm_list_find_next(p, (h), m, (c))) #define nvkm_list_foreach(p,h,m,c) \ for (p = nvkm_list_find(p, (h), m, (c)); p; p = nvkm_list_find_next(p, (h), m, (c))) - -/*FIXME: remove after */ -#define nvkm_fifo_chan nvkm_chan -#define nvkm_fifo_chan_func nvkm_chan_func -#define nvkm_fifo_cgrp nvkm_cgrp #endif diff --git a/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h b/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h index cd86d9198e4a..b7bb8a29a729 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h @@ -3,7 +3,7 @@ #define __NVKM_FLCNEN_H__ #define nvkm_falcon(p) container_of((p), struct nvkm_falcon, engine) #include <core/engine.h> -struct nvkm_fifo_chan; +struct nvkm_chan; enum nvkm_falcon_dmaidx { FALCON_DMAIDX_UCODE = 0, diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h index 01a22a13b452..1755b0df3cc1 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h @@ -59,6 +59,7 @@ struct nvkm_fb { struct nvkm_memory *mmu_wr; }; +u64 nvkm_fb_vidmem_size(struct nvkm_device *); int nvkm_fb_mem_unlock(struct nvkm_fb *); void nvkm_fb_tile_init(struct nvkm_fb *, int region, u32 addr, u32 size, diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index f75c6f09dd2a..68b4fb4bec63 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -619,7 +619,10 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) nouveau_connector_set_encoder(connector, nv_encoder); conn_status = connector_status_connected; - drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid); + + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) + drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid); + goto out; } else { nouveau_connector_set_edid(nv_connector, NULL); @@ -1079,7 +1082,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, case DCB_OUTPUT_TV: return get_slave_funcs(encoder)->mode_valid(encoder, mode); case DCB_OUTPUT_DP: - return nv50_dp_mode_valid(connector, nv_encoder, mode, NULL); + return nv50_dp_mode_valid(nv_encoder, mode, NULL); default: BUG(); return MODE_BAD; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index ec3ffff487fc..99977e5fe716 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -465,7 +465,8 @@ nouveau_display_hpd_work(struct work_struct *work) struct drm_connector *connector; struct drm_connector_list_iter conn_iter; u32 pending; - bool changed = false; + int changed = 0; + struct drm_connector *first_changed_connector = NULL; pm_runtime_get_sync(dev->dev); @@ -509,7 +510,12 @@ nouveau_display_hpd_work(struct work_struct *work) if (old_epoch_counter == connector->epoch_counter) continue; - changed = true; + changed++; + if (!first_changed_connector) { + drm_connector_get(connector); + first_changed_connector = connector; + } + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] status updated from %s to %s (epoch counter %llu->%llu)\n", connector->base.id, connector->name, drm_get_connector_status_name(old_status), @@ -520,9 +526,14 @@ nouveau_display_hpd_work(struct work_struct *work) drm_connector_list_iter_end(&conn_iter); mutex_unlock(&dev->mode_config.mutex); - if (changed) + if (changed == 1) + drm_kms_helper_connector_hotplug_event(first_changed_connector); + else if (changed > 0) drm_kms_helper_hotplug_event(dev); + if (first_changed_connector) + drm_connector_put(first_changed_connector); + pm_runtime_mark_last_busy(drm->dev->dev); noop: pm_runtime_put_autosuspend(dev->dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index d49b4875fc3c..6a4980b2d4d4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -267,8 +267,7 @@ nouveau_dp_irq(struct work_struct *work) * yet) */ enum drm_mode_status -nv50_dp_mode_valid(struct drm_connector *connector, - struct nouveau_encoder *outp, +nv50_dp_mode_valid(struct nouveau_encoder *outp, const struct drm_display_mode *mode, unsigned *out_clock) { diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 40fb9a834918..8325fcf35c5e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1254,10 +1254,7 @@ driver_stub = { .num_ioctls = ARRAY_SIZE(nouveau_ioctls), .fops = &nouveau_driver_fops, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .dumb_create = nouveau_display_dumb_create, .dumb_map_offset = drm_gem_ttm_dumb_map_offset, diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 70c1ad6c4d9d..bcba1a14cfab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -143,8 +143,7 @@ enum nouveau_dp_status { int nouveau_dp_detect(struct nouveau_connector *, struct nouveau_encoder *); bool nouveau_dp_link_check(struct nouveau_connector *); void nouveau_dp_irq(struct work_struct *); -enum drm_mode_status nv50_dp_mode_valid(struct drm_connector *, - struct nouveau_encoder *, +enum drm_mode_status nv50_dp_mode_valid(struct nouveau_encoder *, const struct drm_display_mode *, unsigned *clock); diff --git a/drivers/gpu/drm/nouveau/nvif/mmu.c b/drivers/gpu/drm/nouveau/nvif/mmu.c index 3709cbbc19a1..c9dd3cff49a0 100644 --- a/drivers/gpu/drm/nouveau/nvif/mmu.c +++ b/drivers/gpu/drm/nouveau/nvif/mmu.c @@ -27,6 +27,9 @@ void nvif_mmu_dtor(struct nvif_mmu *mmu) { + if (!nvif_object_constructed(&mmu->object)) + return; + kfree(mmu->kind); kfree(mmu->type); kfree(mmu->heap); diff --git a/drivers/gpu/drm/nouveau/nvkm/core/intr.c b/drivers/gpu/drm/nouveau/nvkm/core/intr.c index e20b7ca218c3..36a747f0039e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/intr.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/intr.c @@ -212,8 +212,8 @@ nvkm_intr(int irq, void *arg) list_for_each_entry(intr, &device->intr.intr, head) { for (leaf = 0; leaf < intr->leaves; leaf++) { if (intr->stat[leaf]) { - nvkm_warn(intr->subdev, "intr%d: %08x\n", - leaf, intr->stat[leaf]); + nvkm_debug(intr->subdev, "intr%d: %08x\n", + leaf, intr->stat[leaf]); nvkm_intr_block_locked(intr, leaf, intr->stat[leaf]); } } diff --git a/drivers/gpu/drm/nouveau/nvkm/core/object.c b/drivers/gpu/drm/nouveau/nvkm/core/object.c index 301a5e5b5f7f..7c554c14e884 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/object.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/object.c @@ -185,7 +185,7 @@ nvkm_object_fini(struct nvkm_object *object, bool suspend) nvif_debug(object, "%s children...\n", action); time = ktime_to_us(ktime_get()); - list_for_each_entry(child, &object->tree, head) { + list_for_each_entry_reverse(child, &object->tree, head) { ret = nvkm_object_fini(child, suspend); if (ret && suspend) goto fail_child; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/ga100.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/ga100.c index 6648ed62daa6..315a69f7fdd1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/ce/ga100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/ga100.c @@ -36,6 +36,15 @@ ga100_ce_intr(struct nvkm_inth *inth) } int +ga100_ce_nonstall(struct nvkm_engine *engine) +{ + struct nvkm_subdev *subdev = &engine->subdev; + struct nvkm_device *device = subdev->device; + + return nvkm_rd32(device, 0x104424 + (subdev->inst * 0x80)) & 0x00000fff; +} + +int ga100_ce_fini(struct nvkm_engine *engine, bool suspend) { nvkm_inth_block(&engine->subdev.inth); @@ -67,6 +76,7 @@ ga100_ce = { .oneinit = ga100_ce_oneinit, .init = ga100_ce_init, .fini = ga100_ce_fini, + .nonstall = ga100_ce_nonstall, .cclass = &gv100_ce_cclass, .sclass = { { -1, -1, AMPERE_DMA_COPY_A }, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/ce/ga102.c index 9f3448ad625f..461b73c7e2e0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/ce/ga102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/ga102.c @@ -28,6 +28,7 @@ ga102_ce = { .oneinit = ga100_ce_oneinit, .init = ga100_ce_init, .fini = ga100_ce_fini, + .nonstall = ga100_ce_nonstall, .cclass = &gv100_ce_cclass, .sclass = { { -1, -1, AMPERE_DMA_COPY_A }, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/ce/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/ce/priv.h index c4c046916fa6..0be72c463b21 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/ce/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/ce/priv.h @@ -3,7 +3,7 @@ #define __NVKM_CE_PRIV_H__ #include <engine/ce.h> -void gt215_ce_intr(struct nvkm_falcon *, struct nvkm_fifo_chan *); +void gt215_ce_intr(struct nvkm_falcon *, struct nvkm_chan *); void gk104_ce_intr(struct nvkm_engine *); void gp100_ce_intr(struct nvkm_engine *); @@ -12,4 +12,5 @@ extern const struct nvkm_object_func gv100_ce_cclass; int ga100_ce_oneinit(struct nvkm_engine *); int ga100_ce_init(struct nvkm_engine *); int ga100_ce_fini(struct nvkm_engine *, bool); +int ga100_ce_nonstall(struct nvkm_engine *); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c index 5ea9a2ff0663..5db37247dc29 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c @@ -283,11 +283,21 @@ nvkm_fifo_oneinit(struct nvkm_engine *engine) } /* Initialise non-stall intr handling. */ - if (fifo->func->nonstall_ctor) { - ret = fifo->func->nonstall_ctor(fifo); - if (ret) { - nvkm_error(subdev, "nonstall %d\n", ret); + if (fifo->func->nonstall) { + if (fifo->func->nonstall_ctor) { + ret = fifo->func->nonstall_ctor(fifo); + if (ret < 0) { + nvkm_error(subdev, "nonstall %d\n", ret); + return ret; + } + } else { + ret = 1; } + + ret = nvkm_event_init(fifo->func->nonstall, &fifo->engine.subdev, 1, ret, + &fifo->nonstall.event); + if (ret) + return ret; } /* Allocate USERD + BAR1 polling area. */ @@ -358,7 +368,6 @@ nvkm_fifo_new_(const struct nvkm_fifo_func *func, struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fifo **pfifo) { struct nvkm_fifo *fifo; - int ret; if (!(fifo = *pfifo = kzalloc(sizeof(*fifo), GFP_KERNEL))) return -ENOMEM; @@ -374,16 +383,5 @@ nvkm_fifo_new_(const struct nvkm_fifo_func *func, struct nvkm_device *device, spin_lock_init(&fifo->lock); mutex_init(&fifo->mutex); - ret = nvkm_engine_ctor(&nvkm_fifo, device, type, inst, true, &fifo->engine); - if (ret) - return ret; - - if (func->nonstall) { - ret = nvkm_event_init(func->nonstall, &fifo->engine.subdev, 1, 1, - &fifo->nonstall.event); - if (ret) - return ret; - } - - return 0; + return nvkm_engine_ctor(&nvkm_fifo, device, type, inst, true, &fifo->engine); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga100.c index 12a5d99d5e77..c56d2a839efb 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga100.c @@ -32,9 +32,6 @@ #include <nvif/class.h> -/*TODO: allocate? */ -#define GA100_FIFO_NONSTALL_VECTOR 0 - static u32 ga100_chan_doorbell_handle(struct nvkm_chan *chan) { @@ -83,7 +80,7 @@ ga100_chan_ramfc_write(struct nvkm_chan *chan, u64 offset, u64 length, u32 devm, nvkm_wo32(chan->inst, 0x0e4, priv ? 0x00000020 : 0x00000000); nvkm_wo32(chan->inst, 0x0e8, chan->id); nvkm_wo32(chan->inst, 0x0f4, 0x00001000 | (priv ? 0x00000100 : 0x00000000)); - nvkm_wo32(chan->inst, 0x0f8, 0x80000000 | GA100_FIFO_NONSTALL_VECTOR); + nvkm_wo32(chan->inst, 0x0f8, 0x80000000 | chan->cgrp->runl->nonstall.vector); nvkm_mo32(chan->inst, 0x218, 0x00000000, 0x00000000); nvkm_done(chan->inst); return 0; @@ -148,8 +145,20 @@ ga100_engn_cxid(struct nvkm_engn *engn, bool *cgid) return -ENODEV; } +static int +ga100_engn_nonstall(struct nvkm_engn *engn) +{ + struct nvkm_engine *engine = engn->engine; + + if (WARN_ON(!engine->func->nonstall)) + return -EINVAL; + + return engine->func->nonstall(engine); +} + const struct nvkm_engn_func ga100_engn = { + .nonstall = ga100_engn_nonstall, .cxid = ga100_engn_cxid, .ctor = gk104_ectx_ctor, .bind = gv100_ectx_bind, @@ -157,6 +166,7 @@ ga100_engn = { const struct nvkm_engn_func ga100_engn_ce = { + .nonstall = ga100_engn_nonstall, .cxid = ga100_engn_cxid, .ctor = gv100_ectx_ce_ctor, .bind = gv100_ectx_ce_bind, @@ -429,7 +439,9 @@ static int ga100_runl_new(struct nvkm_fifo *fifo, int id, u32 addr, struct nvkm_runl **prunl) { struct nvkm_device *device = fifo->engine.subdev.device; + struct nvkm_top_device *tdev; struct nvkm_runl *runl; + struct nvkm_engn *engn; u32 chcfg = nvkm_rd32(device, addr + 0x004); u32 chnum = 1 << (chcfg & 0x0000000f); u32 chaddr = (chcfg & 0xfffffff0); @@ -437,26 +449,55 @@ ga100_runl_new(struct nvkm_fifo *fifo, int id, u32 addr, struct nvkm_runl **prun u32 vector = nvkm_rd32(device, addr + 0x160); int i, ret; - runl = *prunl = nvkm_runl_new(fifo, id, addr, chnum); + runl = nvkm_runl_new(fifo, id, addr, chnum); if (IS_ERR(runl)) return PTR_ERR(runl); + *prunl = runl; + for (i = 0; i < 2; i++) { u32 pbcfg = nvkm_rd32(device, addr + 0x010 + (i * 0x04)); if (pbcfg & 0x80000000) { runl->runq[runl->runq_nr] = nvkm_runq_new(fifo, ((pbcfg & 0x03fffc00) - 0x040000) / 0x800); - if (!runl->runq[runl->runq_nr]) + if (!runl->runq[runl->runq_nr]) { + RUNL_ERROR(runl, "runq %d", runl->runq_nr); return -ENOMEM; + } runl->runq_nr++; } } + nvkm_list_foreach(tdev, &device->top->device, head, tdev->runlist == runl->addr) { + if (tdev->engine < 0) { + RUNL_DEBUG(runl, "engn !top"); + return -EINVAL; + } + + engn = nvkm_runl_add(runl, tdev->engine, (tdev->type == NVKM_ENGINE_CE) ? + fifo->func->engn_ce : fifo->func->engn, + tdev->type, tdev->inst); + if (!engn) + return -EINVAL; + + if (!engn->engine->func->nonstall) { + RUNL_DEBUG(runl, "engn %s !nonstall", engn->engine->subdev.name); + return -EINVAL; + } + } + + if (list_empty(&runl->engns)) { + RUNL_DEBUG(runl, "!engns"); + return -EINVAL; + } + ret = nvkm_inth_add(&device->vfn->intr, vector & 0x00000fff, NVKM_INTR_PRIO_NORMAL, &fifo->engine.subdev, ga100_runl_intr, &runl->inth); - if (ret) + if (ret) { + RUNL_ERROR(runl, "inth %d", ret); return ret; + } runl->chan = chaddr; runl->doorbell = dbcfg >> 16; @@ -466,9 +507,9 @@ ga100_runl_new(struct nvkm_fifo *fifo, int id, u32 addr, struct nvkm_runl **prun static irqreturn_t ga100_fifo_nonstall_intr(struct nvkm_inth *inth) { - struct nvkm_fifo *fifo = container_of(inth, typeof(*fifo), nonstall.intr); + struct nvkm_runl *runl = container_of(inth, typeof(*runl), nonstall.inth); - nvkm_event_ntfy(&fifo->nonstall.event, 0, NVKM_FIFO_NONSTALL_EVENT); + nvkm_event_ntfy(&runl->fifo->nonstall.event, runl->id, NVKM_FIFO_NONSTALL_EVENT); return IRQ_HANDLED; } @@ -476,16 +517,18 @@ static void ga100_fifo_nonstall_block(struct nvkm_event *event, int type, int index) { struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), nonstall.event); + struct nvkm_runl *runl = nvkm_runl_get(fifo, index, 0); - nvkm_inth_block(&fifo->nonstall.intr); + nvkm_inth_block(&runl->nonstall.inth); } static void ga100_fifo_nonstall_allow(struct nvkm_event *event, int type, int index) { struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), nonstall.event); + struct nvkm_runl *runl = nvkm_runl_get(fifo, index, 0); - nvkm_inth_allow(&fifo->nonstall.intr); + nvkm_inth_allow(&runl->nonstall.inth); } const struct nvkm_event_func @@ -497,9 +540,29 @@ ga100_fifo_nonstall = { int ga100_fifo_nonstall_ctor(struct nvkm_fifo *fifo) { - return nvkm_inth_add(&fifo->engine.subdev.device->vfn->intr, GA100_FIFO_NONSTALL_VECTOR, - NVKM_INTR_PRIO_NORMAL, &fifo->engine.subdev, ga100_fifo_nonstall_intr, - &fifo->nonstall.intr); + struct nvkm_subdev *subdev = &fifo->engine.subdev; + struct nvkm_vfn *vfn = subdev->device->vfn; + struct nvkm_runl *runl; + int ret, nr = 0; + + nvkm_runl_foreach(runl, fifo) { + struct nvkm_engn *engn = list_first_entry(&runl->engns, typeof(*engn), head); + + runl->nonstall.vector = engn->func->nonstall(engn); + if (runl->nonstall.vector < 0) { + RUNL_ERROR(runl, "nonstall %d", runl->nonstall.vector); + return runl->nonstall.vector; + } + + ret = nvkm_inth_add(&vfn->intr, runl->nonstall.vector, NVKM_INTR_PRIO_NORMAL, + subdev, ga100_fifo_nonstall_intr, &runl->nonstall.inth); + if (ret) + return ret; + + nr = max(nr, runl->id + 1); + } + + return nr; } int @@ -514,15 +577,13 @@ ga100_fifo_runl_ctor(struct nvkm_fifo *fifo) runl = nvkm_runl_get(fifo, -1, tdev->runlist); if (!runl) { ret = ga100_runl_new(fifo, id++, tdev->runlist, &runl); - if (ret) - return ret; - } - - if (tdev->engine < 0) - continue; + if (ret) { + if (runl) + nvkm_runl_del(runl); - nvkm_runl_add(runl, tdev->engine, (tdev->type == NVKM_ENGINE_CE) ? - fifo->func->engn_ce : fifo->func->engn, tdev->type, tdev->inst); + continue; + } + } } return 0; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.c index 93d628d7d508..454a481a0aef 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.c @@ -399,7 +399,7 @@ nvkm_runl_new(struct nvkm_fifo *fifo, int runi, u32 addr, int id_nr) int ret; if (!(runl = kzalloc(sizeof(*runl), GFP_KERNEL))) - return NULL; + return ERR_PTR(-ENOMEM); runl->func = fifo->func->runl; runl->fifo = fifo; @@ -419,7 +419,7 @@ nvkm_runl_new(struct nvkm_fifo *fifo, int runi, u32 addr, int id_nr) (ret = nvkm_chid_new(&nvkm_chan_event, subdev, id_nr, 0, id_nr, &runl->chid))) { RUNL_ERROR(runl, "cgid/chid: %d", ret); nvkm_runl_del(runl); - return NULL; + return ERR_PTR(ret); } } else { runl->cgid = nvkm_chid_ref(fifo->cgid); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.h index c93d21bb7bd5..5421321f8e85 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.h @@ -11,6 +11,7 @@ enum nvkm_subdev_type; struct nvkm_engn { const struct nvkm_engn_func { + int (*nonstall)(struct nvkm_engn *); bool (*chsw)(struct nvkm_engn *); int (*cxid)(struct nvkm_engn *, bool *cgid); void (*mmu_fault_trigger)(struct nvkm_engn *); @@ -69,6 +70,11 @@ struct nvkm_runl { struct nvkm_inth inth; + struct { + int vector; + struct nvkm_inth inth; + } nonstall; + struct list_head cgrps; int cgrp_nr; int chan_nr; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/uchan.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/uchan.c index 1dac95ae7b43..04140e0110be 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/uchan.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/uchan.c @@ -52,7 +52,7 @@ nvkm_uchan_uevent(struct nvkm_object *object, void *argv, u32 argc, struct nvkm_ switch (args->v0.type) { case NVIF_CHAN_EVENT_V0_NON_STALL_INTR: - return nvkm_uevent_add(uevent, &runl->fifo->nonstall.event, 0, + return nvkm_uevent_add(uevent, &runl->fifo->nonstall.event, runl->id, NVKM_FIFO_NONSTALL_EVENT, NULL); case NVIF_CHAN_EVENT_V0_KILLED: return nvkm_uevent_add(uevent, &runl->chid->event, chan->id, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c index 71b824e6da9d..0096ad401b15 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c @@ -109,8 +109,7 @@ nvkm_gr_oclass_get(struct nvkm_oclass *oclass, int index) } static int -nvkm_gr_cclass_new(struct nvkm_fifo_chan *chan, - const struct nvkm_oclass *oclass, +nvkm_gr_cclass_new(struct nvkm_chan *chan, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nvkm_gr *gr = nvkm_gr(oclass->engine); @@ -127,6 +126,17 @@ nvkm_gr_intr(struct nvkm_engine *engine) } static int +nvkm_gr_nonstall(struct nvkm_engine *engine) +{ + struct nvkm_gr *gr = nvkm_gr(engine); + + if (gr->func->nonstall) + return gr->func->nonstall(gr); + + return -EINVAL; +} + +static int nvkm_gr_oneinit(struct nvkm_engine *engine) { struct nvkm_gr *gr = nvkm_gr(engine); @@ -178,6 +188,7 @@ nvkm_gr = { .init = nvkm_gr_init, .fini = nvkm_gr_fini, .reset = nvkm_gr_reset, + .nonstall = nvkm_gr_nonstall, .intr = nvkm_gr_intr, .tile = nvkm_gr_tile, .chsw_load = nvkm_gr_chsw_load, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ga102.c index a5b5ac2755a2..00cd70abad67 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ga102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ga102.c @@ -137,8 +137,15 @@ ga102_gr_oneinit_intr(struct gf100_gr *gr, enum nvkm_intr_type *pvector) return &device->vfn->intr; } +static int +ga102_gr_nonstall(struct gf100_gr *gr) +{ + return nvkm_rd32(gr->base.engine.subdev.device, 0x400160) & 0x00000fff; +} + static const struct gf100_gr_func ga102_gr = { + .nonstall = ga102_gr_nonstall, .oneinit_intr = ga102_gr_oneinit_intr, .oneinit_tiles = gm200_gr_oneinit_tiles, .oneinit_sm_id = gv100_gr_oneinit_sm_id, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c index 5f20079c3660..3648868bb9fc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c @@ -374,7 +374,7 @@ gf100_gr_chan = { }; static int -gf100_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +gf100_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { @@ -2494,12 +2494,24 @@ gf100_gr_gpccs_ucode = { .data.size = sizeof(gf100_grgpc_data), }; +static int +gf100_gr_nonstall(struct nvkm_gr *base) +{ + struct gf100_gr *gr = gf100_gr(base); + + if (gr->func->nonstall) + return gr->func->nonstall(gr); + + return -EINVAL; +} + static const struct nvkm_gr_func gf100_gr_ = { .dtor = gf100_gr_dtor, .oneinit = gf100_gr_oneinit, .init = gf100_gr_init_, .fini = gf100_gr_fini, + .nonstall = gf100_gr_nonstall, .reset = gf100_gr_reset, .units = gf100_gr_units, .chan_new = gf100_gr_chan_new, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h index 94ca7ac16acf..54f686ba39ac 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h @@ -147,6 +147,7 @@ struct gf100_gr_func_zbc { }; struct gf100_gr_func { + int (*nonstall)(struct gf100_gr *); struct nvkm_intr *(*oneinit_intr)(struct gf100_gr *, enum nvkm_intr_type *); void (*oneinit_tiles)(struct gf100_gr *); int (*oneinit_sm_id)(struct gf100_gr *); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c index 81bd682c2102..ca822f07b63e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c @@ -1181,7 +1181,7 @@ nv04_gr_chan = { }; static int -nv04_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +nv04_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv04_gr *gr = nv04_gr(base); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c index 7fe6e58f6bab..92ef7c9b2910 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c @@ -999,7 +999,7 @@ nv10_gr_chan = { } while (0) int -nv10_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +nv10_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv10_gr *gr = nv10_gr(base); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.h index 5cfe927c9123..b86090c08060 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.h @@ -9,6 +9,6 @@ int nv10_gr_init(struct nvkm_gr *); void nv10_gr_intr(struct nvkm_gr *); void nv10_gr_tile(struct nvkm_gr *, int, struct nvkm_fb_tile *); -int nv10_gr_chan_new(struct nvkm_gr *, struct nvkm_fifo_chan *, +int nv10_gr_chan_new(struct nvkm_gr *, struct nvkm_chan *, const struct nvkm_oclass *, struct nvkm_object **); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c index 75434f5de7ad..02a8c62a0a32 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c @@ -72,7 +72,7 @@ nv20_gr_chan = { }; static int -nv20_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +nv20_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv20_gr *gr = nv20_gr(base); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c index 94685e4d4f87..d6bc6904dcc8 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c @@ -18,7 +18,7 @@ nv25_gr_chan = { }; static int -nv25_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +nv25_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv20_gr *gr = nv20_gr(base); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c index 2d6273675291..e5a351b51eb9 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c @@ -18,7 +18,7 @@ nv2a_gr_chan = { }; static int -nv2a_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +nv2a_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv20_gr *gr = nv20_gr(base); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c index 647bd6fede04..80370323755e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c @@ -19,7 +19,7 @@ nv30_gr_chan = { }; static int -nv30_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +nv30_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv20_gr *gr = nv20_gr(base); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c index 2eae3fe4ef4e..cdf043bbdd59 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c @@ -18,7 +18,7 @@ nv34_gr_chan = { }; static int -nv34_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +nv34_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv20_gr *gr = nv20_gr(base); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c index 657d7cdba369..fa5a6ccb871d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c @@ -18,7 +18,7 @@ nv35_gr_chan = { }; static int -nv35_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +nv35_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv20_gr *gr = nv20_gr(base); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c index d2df097a6cf6..a5e1f02791b4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c @@ -145,7 +145,7 @@ nv40_gr_chan = { }; int -nv40_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +nv40_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv40_gr *gr = nv40_gr(base); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h index f3d3d3a5ae5b..84fbc99139e5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h @@ -22,12 +22,12 @@ u64 nv40_gr_units(struct nvkm_gr *); struct nv40_gr_chan { struct nvkm_object object; struct nv40_gr *gr; - struct nvkm_fifo_chan *fifo; + struct nvkm_chan *fifo; u32 inst; struct list_head head; }; -int nv40_gr_chan_new(struct nvkm_gr *, struct nvkm_fifo_chan *, +int nv40_gr_chan_new(struct nvkm_gr *, struct nvkm_chan *, const struct nvkm_oclass *, struct nvkm_object **); extern const struct nvkm_object_func nv40_gr_object; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c index 1ba18a8e380f..c8a0288c092d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c @@ -86,7 +86,7 @@ nv50_gr_chan = { }; int -nv50_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch, +nv50_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv50_gr *gr = nv50_gr(base); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h index 84388c42e5c6..97ead0042357 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h @@ -27,7 +27,7 @@ struct nv50_gr_chan { struct nv50_gr *gr; }; -int nv50_gr_chan_new(struct nvkm_gr *, struct nvkm_fifo_chan *, +int nv50_gr_chan_new(struct nvkm_gr *, struct nvkm_chan *, const struct nvkm_oclass *, struct nvkm_object **); extern const struct nvkm_object_func nv50_gr_object; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h index 08d5c96e6458..0884abc73a9d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h @@ -5,7 +5,7 @@ #include <engine/gr.h> #include <core/enum.h> struct nvkm_fb_tile; -struct nvkm_fifo_chan; +struct nvkm_chan; int nvkm_gr_ctor(const struct nvkm_gr_func *, struct nvkm_device *, enum nvkm_subdev_type, int, bool enable, struct nvkm_gr *); @@ -18,10 +18,11 @@ struct nvkm_gr_func { int (*init)(struct nvkm_gr *); int (*fini)(struct nvkm_gr *, bool); int (*reset)(struct nvkm_gr *); + int (*nonstall)(struct nvkm_gr *); void (*intr)(struct nvkm_gr *); void (*tile)(struct nvkm_gr *, int region, struct nvkm_fb_tile *); int (*tlb_flush)(struct nvkm_gr *); - int (*chan_new)(struct nvkm_gr *, struct nvkm_fifo_chan *, + int (*chan_new)(struct nvkm_gr *, struct nvkm_chan *, const struct nvkm_oclass *, struct nvkm_object **); int (*object_get)(struct nvkm_gr *, int, struct nvkm_sclass *); /* Returns chipset-specific counts of units packed into an u64. diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c index cb0c3991b2ad..db9fc1ecae0d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c @@ -81,8 +81,7 @@ nv31_mpeg_chan = { }; int -nv31_mpeg_chan_new(struct nvkm_fifo_chan *fifoch, - const struct nvkm_oclass *oclass, +nv31_mpeg_chan_new(struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv31_mpeg *mpeg = nv31_mpeg(oclass->engine); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h index 9f30aaaf809e..251d659565de 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h @@ -24,9 +24,9 @@ struct nv31_mpeg_func { struct nv31_mpeg_chan { struct nvkm_object object; struct nv31_mpeg *mpeg; - struct nvkm_fifo_chan *fifo; + struct nvkm_chan *fifo; }; -int nv31_mpeg_chan_new(struct nvkm_fifo_chan *, const struct nvkm_oclass *, +int nv31_mpeg_chan_new(struct nvkm_chan *, const struct nvkm_oclass *, struct nvkm_object **); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c index 0890a279458e..4b1374adbda3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c @@ -43,7 +43,7 @@ struct nv44_mpeg { struct nv44_mpeg_chan { struct nvkm_object object; struct nv44_mpeg *mpeg; - struct nvkm_fifo_chan *fifo; + struct nvkm_chan *fifo; struct list_head head; u32 inst; }; @@ -100,8 +100,7 @@ nv44_mpeg_chan = { }; static int -nv44_mpeg_chan_new(struct nvkm_fifo_chan *fifoch, - const struct nvkm_oclass *oclass, +nv44_mpeg_chan_new(struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv44_mpeg *mpeg = nv44_mpeg(oclass->engine); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/priv.h index 667a2d05dd89..044ff4133874 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/priv.h @@ -2,7 +2,7 @@ #ifndef __NVKM_MPEG_PRIV_H__ #define __NVKM_MPEG_PRIV_H__ #include <engine/mpeg.h> -struct nvkm_fifo_chan; +struct nvkm_chan; int nv31_mpeg_init(struct nvkm_engine *); void nv31_mpeg_tile(struct nvkm_engine *, int, struct nvkm_fb_tile *); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/base.c index a9d464db6974..20220d6d4a13 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/sw/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/base.c @@ -74,8 +74,7 @@ nvkm_sw_oclass_get(struct nvkm_oclass *oclass, int index) } static int -nvkm_sw_cclass_get(struct nvkm_fifo_chan *fifoch, - const struct nvkm_oclass *oclass, +nvkm_sw_cclass_get(struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nvkm_sw *sw = nvkm_sw(oclass->engine); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.c index 834b8cbed51d..2bf45141de60 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.c @@ -74,7 +74,7 @@ nvkm_sw_chan = { int nvkm_sw_chan_ctor(const struct nvkm_sw_chan_func *func, struct nvkm_sw *sw, - struct nvkm_fifo_chan *fifo, const struct nvkm_oclass *oclass, + struct nvkm_chan *fifo, const struct nvkm_oclass *oclass, struct nvkm_sw_chan *chan) { unsigned long flags; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.h b/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.h index 67b2e5ea93d9..c313aea16a17 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.h @@ -11,7 +11,7 @@ struct nvkm_sw_chan { const struct nvkm_sw_chan_func *func; struct nvkm_object object; struct nvkm_sw *sw; - struct nvkm_fifo_chan *fifo; + struct nvkm_chan *fifo; struct list_head head; #define NVKM_SW_CHAN_EVENT_PAGE_FLIP BIT(0) @@ -24,7 +24,7 @@ struct nvkm_sw_chan_func { }; int nvkm_sw_chan_ctor(const struct nvkm_sw_chan_func *, struct nvkm_sw *, - struct nvkm_fifo_chan *, const struct nvkm_oclass *, + struct nvkm_chan *, const struct nvkm_oclass *, struct nvkm_sw_chan *); bool nvkm_sw_chan_mthd(struct nvkm_sw_chan *, int subc, u32 mthd, u32 data); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c index c3cf6f2ff86c..a0273baf4c67 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c @@ -102,7 +102,7 @@ gf100_sw_chan = { }; static int -gf100_sw_chan_new(struct nvkm_sw *sw, struct nvkm_fifo_chan *fifoch, +gf100_sw_chan_new(struct nvkm_sw *sw, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c index 4aa57573869c..8a1d112da894 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c @@ -106,7 +106,7 @@ nv04_sw_chan = { }; static int -nv04_sw_chan_new(struct nvkm_sw *sw, struct nvkm_fifo_chan *fifo, +nv04_sw_chan_new(struct nvkm_sw *sw, struct nvkm_chan *fifo, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nv04_sw_chan *chan; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c index e79e640ae535..742c75859569 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c @@ -36,7 +36,7 @@ nv10_sw_chan = { }; static int -nv10_sw_chan_new(struct nvkm_sw *sw, struct nvkm_fifo_chan *fifo, +nv10_sw_chan_new(struct nvkm_sw *sw, struct nvkm_chan *fifo, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nvkm_sw_chan *chan; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c index 9d7a9b7d5be3..99476d32c5af 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c @@ -99,7 +99,7 @@ nv50_sw_chan = { }; static int -nv50_sw_chan_new(struct nvkm_sw *sw, struct nvkm_fifo_chan *fifoch, +nv50_sw_chan_new(struct nvkm_sw *sw, struct nvkm_chan *fifoch, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { struct nvkm_disp *disp = sw->engine.subdev.device->disp; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sw/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/sw/priv.h index d9d83b1b8849..8015afaba947 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/sw/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sw/priv.h @@ -15,7 +15,7 @@ struct nvkm_sw_chan_sclass { }; struct nvkm_sw_func { - int (*chan_new)(struct nvkm_sw *, struct nvkm_fifo_chan *, + int (*chan_new)(struct nvkm_sw *, struct nvkm_chan *, const struct nvkm_oclass *, struct nvkm_object **); const struct nvkm_sw_chan_sclass sclass[]; }; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild index 6ba5120a2ebe..394c305e759a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild @@ -55,7 +55,7 @@ nvkm-y += nvkm/subdev/fb/ramgk104.o nvkm-y += nvkm/subdev/fb/ramgm107.o nvkm-y += nvkm/subdev/fb/ramgm200.o nvkm-y += nvkm/subdev/fb/ramgp100.o -nvkm-y += nvkm/subdev/fb/ramga102.o +nvkm-y += nvkm/subdev/fb/ramgp102.o nvkm-y += nvkm/subdev/fb/sddr2.o nvkm-y += nvkm/subdev/fb/sddr3.o nvkm-y += nvkm/subdev/fb/gddr3.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c index 0955340cc421..8a286a9349ac 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c @@ -174,6 +174,18 @@ nvkm_fb_mem_unlock(struct nvkm_fb *fb) return 0; } +u64 +nvkm_fb_vidmem_size(struct nvkm_device *device) +{ + struct nvkm_fb *fb = device->fb; + + if (fb && fb->func->vidmem.size) + return fb->func->vidmem.size(fb); + + WARN_ON(1); + return 0; +} + static int nvkm_fb_init(struct nvkm_subdev *subdev) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c index a7456e786463..12037fd4fdf2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c @@ -30,7 +30,8 @@ ga100_fb = { .init_page = gv100_fb_init_page, .init_unkn = gp100_fb_init_unkn, .sysmem.flush_page_init = gf100_fb_sysmem_flush_page_init, - .ram_new = gp100_ram_new, + .vidmem.size = gp102_fb_vidmem_size, + .ram_new = gp102_ram_new, .default_bigpage = 16, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c index dd476e079fe1..76f6877b54c6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c @@ -24,6 +24,12 @@ #include <engine/nvdec.h> +static u64 +ga102_fb_vidmem_size(struct nvkm_fb *fb) +{ + return (u64)nvkm_rd32(fb->subdev.device, 0x1183a4) << 20; +} + static int ga102_fb_oneinit(struct nvkm_fb *fb) { @@ -43,7 +49,8 @@ ga102_fb = { .init_page = gv100_fb_init_page, .init_unkn = gp100_fb_init_unkn, .sysmem.flush_page_init = gf100_fb_sysmem_flush_page_init, - .ram_new = ga102_ram_new, + .vidmem.size = ga102_fb_vidmem_size, + .ram_new = gp102_ram_new, .default_bigpage = 16, .vpr.scrub_required = tu102_fb_vpr_scrub_required, .vpr.scrub = gp102_fb_vpr_scrub, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp102.c index 14d942e8b857..534553c64805 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp102.c @@ -40,6 +40,20 @@ gp102_fb_vpr_scrub_required(struct nvkm_fb *fb) return (nvkm_rd32(device, 0x100cd0) & 0x00000010) != 0; } +u64 +gp102_fb_vidmem_size(struct nvkm_fb *fb) +{ + const u32 data = nvkm_rd32(fb->subdev.device, 0x100ce0); + const u32 lmag = (data & 0x000003f0) >> 4; + const u32 lsca = (data & 0x0000000f); + const u64 size = (u64)lmag << (lsca + 20); + + if (data & 0x40000000) + return size / 16 * 15; + + return size; +} + int gp102_fb_oneinit(struct nvkm_fb *fb) { @@ -59,9 +73,10 @@ gp102_fb = { .init_remapper = gp100_fb_init_remapper, .init_page = gm200_fb_init_page, .sysmem.flush_page_init = gf100_fb_sysmem_flush_page_init, + .vidmem.size = gp102_fb_vidmem_size, .vpr.scrub_required = gp102_fb_vpr_scrub_required, .vpr.scrub = gp102_fb_vpr_scrub, - .ram_new = gp100_ram_new, + .ram_new = gp102_ram_new, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c index 4d8a286a7a34..f422564bee5b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c @@ -36,9 +36,10 @@ gv100_fb = { .init_page = gv100_fb_init_page, .init_unkn = gp100_fb_init_unkn, .sysmem.flush_page_init = gf100_fb_sysmem_flush_page_init, + .vidmem.size = gp102_fb_vidmem_size, .vpr.scrub_required = gp102_fb_vpr_scrub_required, .vpr.scrub = gp102_fb_vpr_scrub, - .ram_new = gp100_ram_new, + .ram_new = gp102_ram_new, .default_bigpage = 16, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h index 726c30c8bf95..77d6a8c10829 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h @@ -20,6 +20,10 @@ struct nvkm_fb_func { void (*flush_page_init)(struct nvkm_fb *); } sysmem; + struct nvkm_fb_func_vidmem { + u64 (*size)(struct nvkm_fb *); + } vidmem; + struct { bool (*scrub_required)(struct nvkm_fb *); int (*scrub)(struct nvkm_fb *); @@ -84,6 +88,7 @@ void gp100_fb_init_remapper(struct nvkm_fb *); void gp100_fb_init_unkn(struct nvkm_fb *); int gp102_fb_oneinit(struct nvkm_fb *); +u64 gp102_fb_vidmem_size(struct nvkm_fb *); bool gp102_fb_vpr_scrub_required(struct nvkm_fb *); int gp102_fb_vpr_scrub(struct nvkm_fb *); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h index ea7d66f3dd82..50f0c1914f58 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h @@ -70,5 +70,5 @@ int gk104_ram_new(struct nvkm_fb *, struct nvkm_ram **); int gm107_ram_new(struct nvkm_fb *, struct nvkm_ram **); int gm200_ram_new(struct nvkm_fb *, struct nvkm_ram **); int gp100_ram_new(struct nvkm_fb *, struct nvkm_ram **); -int ga102_ram_new(struct nvkm_fb *, struct nvkm_ram **); +int gp102_ram_new(struct nvkm_fb *, struct nvkm_ram **); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c deleted file mode 100644 index 298c136cefe0..000000000000 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2021 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ -#include "ram.h" - -#include <subdev/bios.h> -#include <subdev/bios/init.h> -#include <subdev/bios/rammap.h> - -static const struct nvkm_ram_func -ga102_ram = { -}; - -int -ga102_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) -{ - struct nvkm_device *device = fb->subdev.device; - enum nvkm_ram_type type = nvkm_fb_bios_memtype(device->bios); - u32 size = nvkm_rd32(device, 0x1183a4); - - return nvkm_ram_new_(&ga102_ram, fb, type, (u64)size << 20, pram); -} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp102.c new file mode 100644 index 000000000000..8550f5e47347 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp102.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +#include "ram.h" + +#include <subdev/bios.h> + +static const struct nvkm_ram_func +gp102_ram = { +}; + +int +gp102_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + enum nvkm_ram_type type = nvkm_fb_bios_memtype(fb->subdev.device->bios); + const u32 rsvd_head = ( 256 * 1024); /* vga memory */ + const u32 rsvd_tail = (1024 * 1024); /* vbios etc */ + u64 size = fb->func->vidmem.size(fb); + int ret; + + ret = nvkm_ram_new_(&gp102_ram, fb, type, size, pram); + if (ret) + return ret; + + nvkm_mm_fini(&(*pram)->vram); + + return nvkm_mm_init(&(*pram)->vram, NVKM_RAM_MM_NORMAL, + rsvd_head >> NVKM_RAM_MM_SHIFT, + (size - rsvd_head - rsvd_tail) >> NVKM_RAM_MM_SHIFT, + 1); + +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/tu102.c index b8803c124c3b..bcc23d4c8115 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/tu102.c @@ -36,9 +36,10 @@ tu102_fb = { .init_page = gv100_fb_init_page, .init_unkn = gp100_fb_init_unkn, .sysmem.flush_page_init = gf100_fb_sysmem_flush_page_init, + .vidmem.size = gp102_fb_vidmem_size, .vpr.scrub_required = tu102_fb_vpr_scrub_required, .vpr.scrub = gp102_fb_vpr_scrub, - .ram_new = gp100_ram_new, + .ram_new = gp102_ram_new, .default_bigpage = 16, }; diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c index aacad5045e95..c26aab4939fa 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c @@ -4858,10 +4858,9 @@ static int dispc_probe(struct platform_device *pdev) return component_add(&pdev->dev, &dispc_component_ops); } -static int dispc_remove(struct platform_device *pdev) +static void dispc_remove(struct platform_device *pdev) { component_del(&pdev->dev, &dispc_component_ops); - return 0; } static __maybe_unused int dispc_runtime_suspend(struct device *dev) @@ -4913,7 +4912,7 @@ static const struct dev_pm_ops dispc_pm_ops = { struct platform_driver omap_dispchw_driver = { .probe = dispc_probe, - .remove = dispc_remove, + .remove_new = dispc_remove, .driver = { .name = "omapdss_dispc", .pm = &dispc_pm_ops, diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index 4c1084eb0175..ea63c64d3a1a 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -5044,7 +5044,7 @@ err_pm_disable: return r; } -static int dsi_remove(struct platform_device *pdev) +static void dsi_remove(struct platform_device *pdev) { struct dsi_data *dsi = platform_get_drvdata(pdev); @@ -5060,8 +5060,6 @@ static int dsi_remove(struct platform_device *pdev) regulator_disable(dsi->vdds_dsi_reg); dsi->vdds_dsi_enabled = false; } - - return 0; } static __maybe_unused int dsi_runtime_suspend(struct device *dev) @@ -5095,7 +5093,7 @@ static const struct dev_pm_ops dsi_pm_ops = { struct platform_driver omap_dsihw_driver = { .probe = dsi_probe, - .remove = dsi_remove, + .remove_new = dsi_remove, .driver = { .name = "omapdss_dsi", .pm = &dsi_pm_ops, diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c b/drivers/gpu/drm/omapdrm/dss/dss.c index c4febb861910..02955f976845 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.c +++ b/drivers/gpu/drm/omapdrm/dss/dss.c @@ -1532,7 +1532,7 @@ err_free_dss: return r; } -static int dss_remove(struct platform_device *pdev) +static void dss_remove(struct platform_device *pdev) { struct dss_device *dss = platform_get_drvdata(pdev); @@ -1557,8 +1557,6 @@ static int dss_remove(struct platform_device *pdev) dss_put_clocks(dss); kfree(dss); - - return 0; } static void dss_shutdown(struct platform_device *pdev) @@ -1607,7 +1605,7 @@ static const struct dev_pm_ops dss_pm_ops = { struct platform_driver omap_dsshw_driver = { .probe = dss_probe, - .remove = dss_remove, + .remove_new = dss_remove, .shutdown = dss_shutdown, .driver = { .name = "omapdss_dss", diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index a8a75dc24751..a26b77d99d52 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -824,7 +824,7 @@ err_free: return r; } -static int hdmi4_remove(struct platform_device *pdev) +static void hdmi4_remove(struct platform_device *pdev) { struct omap_hdmi *hdmi = platform_get_drvdata(pdev); @@ -835,7 +835,6 @@ static int hdmi4_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); kfree(hdmi); - return 0; } static const struct of_device_id hdmi_of_match[] = { @@ -845,7 +844,7 @@ static const struct of_device_id hdmi_of_match[] = { struct platform_driver omapdss_hdmi4hw_driver = { .probe = hdmi4_probe, - .remove = hdmi4_remove, + .remove_new = hdmi4_remove, .driver = { .name = "omapdss_hdmi", .of_match_table = hdmi_of_match, diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 868712cd8a3a..e6611c683857 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -798,7 +798,7 @@ err_free: return r; } -static int hdmi5_remove(struct platform_device *pdev) +static void hdmi5_remove(struct platform_device *pdev) { struct omap_hdmi *hdmi = platform_get_drvdata(pdev); @@ -809,7 +809,6 @@ static int hdmi5_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); kfree(hdmi); - return 0; } static const struct of_device_id hdmi_of_match[] = { @@ -820,7 +819,7 @@ static const struct of_device_id hdmi_of_match[] = { struct platform_driver omapdss_hdmi5hw_driver = { .probe = hdmi5_probe, - .remove = hdmi5_remove, + .remove_new = hdmi5_remove, .driver = { .name = "omapdss_hdmi5", .of_match_table = hdmi_of_match, diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index 4480b69ab5a7..f163d52a7c7d 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -865,7 +865,7 @@ err_free: return r; } -static int venc_remove(struct platform_device *pdev) +static void venc_remove(struct platform_device *pdev) { struct venc_device *venc = platform_get_drvdata(pdev); @@ -876,7 +876,6 @@ static int venc_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); kfree(venc); - return 0; } static __maybe_unused int venc_runtime_suspend(struct device *dev) @@ -913,7 +912,7 @@ static const struct of_device_id venc_of_match[] = { struct platform_driver omap_venchw_driver = { .probe = venc_probe, - .remove = venc_remove, + .remove_new = venc_remove, .driver = { .name = "omapdss_venc", .pm = &venc_pm_ops, diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index 61a27dd7392e..9753c1e1f994 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -723,7 +723,7 @@ bool dmm_is_available(void) return omap_dmm ? true : false; } -static int omap_dmm_remove(struct platform_device *dev) +static void omap_dmm_remove(struct platform_device *dev) { struct tiler_block *block, *_block; int i; @@ -763,8 +763,6 @@ static int omap_dmm_remove(struct platform_device *dev) kfree(omap_dmm); omap_dmm = NULL; } - - return 0; } static int omap_dmm_probe(struct platform_device *dev) @@ -982,8 +980,7 @@ static int omap_dmm_probe(struct platform_device *dev) return 0; fail: - if (omap_dmm_remove(dev)) - dev_err(&dev->dev, "cleanup failed\n"); + omap_dmm_remove(dev); return ret; } @@ -1213,7 +1210,7 @@ static const struct of_device_id dmm_of_match[] = { struct platform_driver omap_dmm_driver = { .probe = omap_dmm_probe, - .remove = omap_dmm_remove, + .remove_new = omap_dmm_remove, .driver = { .owner = THIS_MODULE, .name = DMM_DRIVER_NAME, diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 5ed549726104..e2697fe80e62 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -655,8 +655,6 @@ static const struct drm_driver omap_drm_driver = { #ifdef CONFIG_DEBUG_FS .debugfs_init = omap_debugfs_init, #endif - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = omap_gem_prime_import, .dumb_create = omap_gem_dumb_create, .dumb_map_offset = omap_gem_dumb_map_offset, @@ -821,14 +819,12 @@ static int pdev_probe(struct platform_device *pdev) return ret; } -static int pdev_remove(struct platform_device *pdev) +static void pdev_remove(struct platform_device *pdev) { struct omap_drm_private *priv = platform_get_drvdata(pdev); omapdrm_cleanup(priv); kfree(priv); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -859,7 +855,7 @@ static struct platform_driver pdev = { .pm = &omapdrm_pm_ops, }, .probe = pdev_probe, - .remove = pdev_remove, + .remove_new = pdev_remove, }; static struct platform_driver * const drivers[] = { diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index 3abc47521b2c..8e194dbc9506 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -66,8 +66,6 @@ static int omap_gem_dmabuf_mmap(struct dma_buf *buffer, struct drm_gem_object *obj = buffer->priv; int ret = 0; - dma_resv_assert_held(buffer->resv); - ret = drm_gem_mmap_obj(obj, omap_gem_mmap_size(obj), vma); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 203c0ef0bbfd..1a0fd0754692 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -793,6 +793,15 @@ config DRM_PANEL_VISIONOX_VTDR6130 Say Y here if you want to enable support for Visionox VTDR6130 1080x2400 AMOLED DSI panel. +config DRM_PANEL_VISIONOX_R66451 + tristate "Visionox R66451" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for Visionox + R66451 1080x2340 AMOLED DSI panel. + config DRM_PANEL_WIDECHIPS_WS2401 tristate "Widechips WS2401 DPI panel driver" depends on SPI && GPIOLIB diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 30cf553c8d1d..499e38244253 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -81,5 +81,6 @@ obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o obj-$(CONFIG_DRM_PANEL_VISIONOX_VTDR6130) += panel-visionox-vtdr6130.o +obj-$(CONFIG_DRM_PANEL_VISIONOX_R66451) += panel-visionox-r66451.o obj-$(CONFIG_DRM_PANEL_WIDECHIPS_WS2401) += panel-widechips-ws2401.o obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index fbd114b4f0be..df7e3cff004c 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1866,6 +1866,7 @@ static const struct panel_delay delay_200_500_e200 = { */ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x1062, &delay_200_500_e50, "B120XAN01.0"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x145c, &delay_200_500_e50, "B116XAB01.4"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x1e9b, &delay_200_500_e50, "B133UAN02.1"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x1ea5, &delay_200_500_e50, "B116XAK01.6"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01"), diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c index de8758c30e6e..b47c3a679be0 100644 --- a/drivers/gpu/drm/panel/panel-lvds.c +++ b/drivers/gpu/drm/panel/panel-lvds.c @@ -228,15 +228,13 @@ static int panel_lvds_probe(struct platform_device *pdev) return 0; } -static int panel_lvds_remove(struct platform_device *pdev) +static void panel_lvds_remove(struct platform_device *pdev) { struct panel_lvds *lvds = platform_get_drvdata(pdev); drm_panel_remove(&lvds->panel); drm_panel_disable(&lvds->panel); - - return 0; } static const struct of_device_id panel_lvds_of_table[] = { @@ -248,7 +246,7 @@ MODULE_DEVICE_TABLE(of, panel_lvds_of_table); static struct platform_driver panel_lvds_driver = { .probe = panel_lvds_probe, - .remove = panel_lvds_remove, + .remove_new = panel_lvds_remove, .driver = { .name = "panel-lvds", .of_match_table = panel_lvds_of_table, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c index 102e1fc7ee38..fec0d014fd0e 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c @@ -66,7 +66,6 @@ static void s6d7aa0_reset(struct s6d7aa0 *ctx) static int s6d7aa0_lock(struct s6d7aa0 *ctx, bool lock) { struct mipi_dsi_device *dsi = ctx->dsi; - int ret = 0; if (lock) { mipi_dsi_dcs_write_seq(dsi, MCS_PASSWD1, 0xa5, 0xa5); @@ -80,7 +79,7 @@ static int s6d7aa0_lock(struct s6d7aa0 *ctx, bool lock) mipi_dsi_dcs_write_seq(dsi, MCS_PASSWD3, 0xa5, 0xa5); } - return ret; + return 0; } static int s6d7aa0_on(struct s6d7aa0 *ctx) diff --git a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c index c250ca36a5b3..658c7c040570 100644 --- a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c +++ b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c @@ -278,14 +278,12 @@ static int seiko_panel_probe(struct device *dev, return 0; } -static int seiko_panel_remove(struct platform_device *pdev) +static void seiko_panel_remove(struct platform_device *pdev) { struct seiko_panel *panel = platform_get_drvdata(pdev); drm_panel_remove(&panel->base); drm_panel_disable(&panel->base); - - return 0; } static void seiko_panel_shutdown(struct platform_device *pdev) @@ -347,7 +345,7 @@ static struct platform_driver seiko_panel_platform_driver = { .of_match_table = platform_of_match, }, .probe = seiko_panel_platform_probe, - .remove = seiko_panel_remove, + .remove_new = seiko_panel_remove, .shutdown = seiko_panel_shutdown, }; module_platform_driver(seiko_panel_platform_driver); diff --git a/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c b/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c index a07d0f6c3e69..76bd9e810827 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c @@ -189,15 +189,13 @@ static int ls037v7dw01_probe(struct platform_device *pdev) return 0; } -static int ls037v7dw01_remove(struct platform_device *pdev) +static void ls037v7dw01_remove(struct platform_device *pdev) { struct ls037v7dw01_panel *lcd = platform_get_drvdata(pdev); drm_panel_remove(&lcd->panel); drm_panel_disable(&lcd->panel); drm_panel_unprepare(&lcd->panel); - - return 0; } static const struct of_device_id ls037v7dw01_of_match[] = { @@ -209,7 +207,7 @@ MODULE_DEVICE_TABLE(of, ls037v7dw01_of_match); static struct platform_driver ls037v7dw01_driver = { .probe = ls037v7dw01_probe, - .remove = ls037v7dw01_remove, + .remove_new = ls037v7dw01_remove, .driver = { .name = "panel-sharp-ls037v7dw01", .of_match_table = ls037v7dw01_of_match, diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index aaba36b3a674..18dc251749fb 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -4468,11 +4468,9 @@ static int panel_simple_platform_probe(struct platform_device *pdev) return panel_simple_probe(&pdev->dev, id->data); } -static int panel_simple_platform_remove(struct platform_device *pdev) +static void panel_simple_platform_remove(struct platform_device *pdev) { panel_simple_remove(&pdev->dev); - - return 0; } static void panel_simple_platform_shutdown(struct platform_device *pdev) @@ -4493,7 +4491,7 @@ static struct platform_driver panel_simple_platform_driver = { .pm = &panel_simple_pm_ops, }, .probe = panel_simple_platform_probe, - .remove = panel_simple_platform_remove, + .remove_new = panel_simple_platform_remove, .shutdown = panel_simple_platform_shutdown, }; diff --git a/drivers/gpu/drm/panel/panel-visionox-r66451.c b/drivers/gpu/drm/panel/panel-visionox-r66451.c new file mode 100644 index 000000000000..00fc28ad3d07 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-visionox-r66451.c @@ -0,0 +1,390 @@ +//SPDX-License-Identifier: GPL-2.0-only +//Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/display/drm_dsc.h> +#include <drm/display/drm_dsc_helper.h> + +#include <video/mipi_display.h> + +struct visionox_r66451 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[2]; + bool prepared, enabled; +}; + +static inline struct visionox_r66451 *to_visionox_r66451(struct drm_panel *panel) +{ + return container_of(panel, struct visionox_r66451, panel); +} + +static void visionox_r66451_reset(struct visionox_r66451 *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(10000, 10100); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(10000, 10100); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(10000, 10100); +} + +static int visionox_r66451_on(struct visionox_r66451 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x00); + mipi_dsi_dcs_write_seq(dsi, 0xc2, + 0x09, 0x24, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x09, 0x3c); + mipi_dsi_dcs_write_seq(dsi, 0xd7, + 0x00, 0xb9, 0x3c, 0x00, 0x40, 0x04, 0x00, 0xa0, 0x0a, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, + 0x3c, 0x00, 0x40, 0x04, 0x00, 0xa0, 0x0a); + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x80); + mipi_dsi_dcs_write_seq(dsi, 0xde, + 0x40, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, + 0x10, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x02, 0x00, 0x00); + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x04); + mipi_dsi_dcs_write_seq(dsi, 0xe8, 0x00, 0x02); + mipi_dsi_dcs_write_seq(dsi, 0xe4, 0x00, 0x08); + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x00); + mipi_dsi_dcs_write_seq(dsi, 0xc4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x32); + mipi_dsi_dcs_write_seq(dsi, 0xcf, + 0x64, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x0b, 0x77, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x03); + mipi_dsi_dcs_write_seq(dsi, 0xd3, + 0x45, 0x00, 0x00, 0x01, 0x13, 0x15, 0x00, 0x15, 0x07, + 0x0f, 0x77, 0x77, 0x77, 0x37, 0xb2, 0x11, 0x00, 0xa0, + 0x3c, 0x9c); + mipi_dsi_dcs_write_seq(dsi, 0xd7, + 0x00, 0xb9, 0x34, 0x00, 0x40, 0x04, 0x00, 0xa0, 0x0a, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, + 0x34, 0x00, 0x40, 0x04, 0x00, 0xa0, 0x0a); + mipi_dsi_dcs_write_seq(dsi, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x3a, 0x00, 0x3a, 0x00, 0x3a, 0x00, 0x3a, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x32, 0x00, 0x0a, 0x00, 0x22); + mipi_dsi_dcs_write_seq(dsi, 0xdf, + 0x50, 0x42, 0x58, 0x81, 0x2d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x0f, 0xff, 0xd4, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x53, 0xf1, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + mipi_dsi_dcs_write_seq(dsi, 0xf7, 0x01); + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x80); + mipi_dsi_dcs_write_seq(dsi, 0xe4, 0x34, 0xb4, 0x00, 0x00, 0x00, 0x39, 0x04, 0x09, 0x34); + mipi_dsi_dcs_write_seq(dsi, 0xe6, 0x00); + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x04); + mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x50, 0x40); + mipi_dsi_dcs_write_seq(dsi, 0xf3, 0x50, 0x00, 0x00, 0x00, 0x00); + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x11); + mipi_dsi_dcs_write_seq(dsi, 0xf3, 0x01, 0x00, 0x00, 0x00, 0x01); + mipi_dsi_dcs_write_seq(dsi, 0xf4, 0x00, 0x02); + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x19); + mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x50, 0x42); + mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + mipi_dsi_dcs_set_column_address(dsi, 0, 1080 - 1); + mipi_dsi_dcs_set_page_address(dsi, 0, 2340 - 1); + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + return 0; +} + +static int visionox_r66451_off(struct visionox_r66451 *ctx) +{ + ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + return 0; +} + +static int visionox_r66451_prepare(struct drm_panel *panel) +{ + struct visionox_r66451 *ctx = to_visionox_r66451(panel); + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + if (ctx->prepared) + return 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret < 0) + return ret; + + visionox_r66451_reset(ctx); + + ret = visionox_r66451_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + return ret; + } + + mipi_dsi_compression_mode(ctx->dsi, true); + + ctx->prepared = true; + return 0; +} + +static int visionox_r66451_unprepare(struct drm_panel *panel) +{ + struct visionox_r66451 *ctx = to_visionox_r66451(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (!ctx->prepared) + return 0; + + ret = visionox_r66451_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + + ctx->prepared = false; + return 0; +} + +static const struct drm_display_mode visionox_r66451_mode = { + .clock = 345830, + .hdisplay = 1080, + .hsync_start = 1175, + .hsync_end = 1176, + .htotal = 1216, + .vdisplay = 2340, + .vsync_start = 2365, + .vsync_end = 2366, + .vtotal = 2370, + .width_mm = 0, + .height_mm = 0, + .type = DRM_MODE_TYPE_DRIVER, +}; + +static int visionox_r66451_enable(struct drm_panel *panel) +{ + struct visionox_r66451 *ctx = to_visionox_r66451(panel); + struct mipi_dsi_device *dsi = ctx->dsi; + struct drm_dsc_picture_parameter_set pps; + int ret; + + if (ctx->enabled) + return 0; + + if (!dsi->dsc) { + dev_err(&dsi->dev, "DSC not attached to DSI\n"); + return -ENODEV; + } + + drm_dsc_pps_payload_pack(&pps, dsi->dsc); + ret = mipi_dsi_picture_parameter_set(dsi, &pps); + if (ret) { + dev_err(&dsi->dev, "Failed to set PPS\n"); + return ret; + } + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(&dsi->dev, "Failed to exit sleep mode: %d\n", ret); + return ret; + } + msleep(120); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(&dsi->dev, "Failed on set display on: %d\n", ret); + return ret; + } + msleep(20); + + ctx->enabled = true; + + return 0; +} + +static int visionox_r66451_disable(struct drm_panel *panel) +{ + struct visionox_r66451 *ctx = to_visionox_r66451(panel); + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + ctx->enabled = false; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display off: %d\n", ret); + return ret; + } + msleep(20); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to enter sleep mode: %d\n", ret); + return ret; + } + msleep(120); + + return 0; +} + +static int visionox_r66451_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + drm_connector_helper_get_modes_fixed(connector, &visionox_r66451_mode); + return 1; +} + +static const struct drm_panel_funcs visionox_r66451_funcs = { + .prepare = visionox_r66451_prepare, + .unprepare = visionox_r66451_unprepare, + .get_modes = visionox_r66451_get_modes, + .enable = visionox_r66451_enable, + .disable = visionox_r66451_disable, +}; + +static int visionox_r66451_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness = backlight_get_brightness(bl); + + return mipi_dsi_dcs_set_display_brightness(dsi, brightness); +} + +static const struct backlight_ops visionox_r66451_bl_ops = { + .update_status = visionox_r66451_bl_update_status, +}; + +static struct backlight_device * +visionox_r66451_create_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + const struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .brightness = 255, + .max_brightness = 4095, + }; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &visionox_r66451_bl_ops, &props); +} + +static int visionox_r66451_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct visionox_r66451 *ctx; + struct drm_dsc_config *dsc; + int ret = 0; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + dsc = devm_kzalloc(dev, sizeof(*dsc), GFP_KERNEL); + if (!dsc) + return -ENOMEM; + + /* Set DSC params */ + dsc->dsc_version_major = 0x1; + dsc->dsc_version_minor = 0x2; + + dsc->slice_height = 20; + dsc->slice_width = 540; + dsc->slice_count = 2; + dsc->bits_per_component = 8; + dsc->bits_per_pixel = 8 << 4; + dsc->block_pred_enable = true; + + dsi->dsc = dsc; + + ctx->supplies[0].supply = "vddio"; + ctx->supplies[1].supply = "vdd"; + + ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + + if (ret < 0) + return ret; + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset-gpios\n"); + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; + + drm_panel_init(&ctx->panel, dev, &visionox_r66451_funcs, DRM_MODE_CONNECTOR_DSI); + ctx->panel.backlight = visionox_r66451_create_backlight(dsi); + if (IS_ERR(ctx->panel.backlight)) + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), + "Failed to create backlight\n"); + + drm_panel_add(&ctx->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + drm_panel_remove(&ctx->panel); + } + + return ret; +} + +static void visionox_r66451_remove(struct mipi_dsi_device *dsi) +{ + struct visionox_r66451 *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "Failed to detach DSI host: %d\n", ret); + + drm_panel_remove(&ctx->panel); +} + +static const struct of_device_id visionox_r66451_of_match[] = { + {.compatible = "visionox,r66451"}, + { /*sentinel*/ } +}; +MODULE_DEVICE_TABLE(of, visionox_r66451_of_match); + +static struct mipi_dsi_driver visionox_r66451_driver = { + .probe = visionox_r66451_probe, + .remove = visionox_r66451_remove, + .driver = { + .name = "panel-visionox-r66451", + .of_match_table = visionox_r66451_of_match, + }, +}; + +module_mipi_dsi_driver(visionox_r66451_driver); + +MODULE_AUTHOR("Jessica Zhang <quic_jesszhan@quicinc.com>"); +MODULE_DESCRIPTION("Panel driver for the Visionox R66451 AMOLED DSI panel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index bbada731bbbd..49b51f0db9b4 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -407,6 +407,10 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data, bo = to_panfrost_bo(gem_obj); + ret = dma_resv_lock_interruptible(bo->base.base.resv, NULL); + if (ret) + goto out_put_object; + mutex_lock(&pfdev->shrinker_lock); mutex_lock(&bo->mappings.lock); if (args->madv == PANFROST_MADV_DONTNEED) { @@ -444,7 +448,8 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data, out_unlock_mappings: mutex_unlock(&bo->mappings.lock); mutex_unlock(&pfdev->shrinker_lock); - + dma_resv_unlock(bo->base.base.resv); +out_put_object: drm_gem_object_put(gem_obj); return ret; } @@ -539,10 +544,7 @@ static const struct drm_driver panfrost_drm_driver = { .minor = 2, .gem_create_object = panfrost_gem_create_object, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, }; static int panfrost_probe(struct platform_device *pdev) @@ -611,7 +613,7 @@ err_out0: return err; } -static int panfrost_remove(struct platform_device *pdev) +static void panfrost_remove(struct platform_device *pdev) { struct panfrost_device *pfdev = platform_get_drvdata(pdev); struct drm_device *ddev = pfdev->ddev; @@ -625,7 +627,6 @@ static int panfrost_remove(struct platform_device *pdev) pm_runtime_set_suspended(pfdev->dev); drm_dev_put(ddev); - return 0; } /* @@ -717,7 +718,7 @@ MODULE_DEVICE_TABLE(of, dt_match); static struct platform_driver panfrost_driver = { .probe = panfrost_probe, - .remove = panfrost_remove, + .remove_new = panfrost_remove, .driver = { .name = "panfrost", .pm = pm_ptr(&panfrost_pm_ops), diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c index bf0170782f25..6a71a2555f85 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c +++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c @@ -48,14 +48,14 @@ static bool panfrost_gem_purge(struct drm_gem_object *obj) if (!mutex_trylock(&bo->mappings.lock)) return false; - if (!mutex_trylock(&shmem->pages_lock)) + if (!dma_resv_trylock(shmem->base.resv)) goto unlock_mappings; panfrost_gem_teardown_mappings_locked(bo); - drm_gem_shmem_purge_locked(&bo->base); + drm_gem_shmem_purge(&bo->base); ret = true; - mutex_unlock(&shmem->pages_lock); + dma_resv_unlock(shmem->base.resv); unlock_mappings: mutex_unlock(&bo->mappings.lock); diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index e961fa27702c..c0123d09f699 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -443,6 +443,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, struct panfrost_gem_mapping *bomapping; struct panfrost_gem_object *bo; struct address_space *mapping; + struct drm_gem_object *obj; pgoff_t page_offset; struct sg_table *sgt; struct page **pages; @@ -465,15 +466,16 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, page_offset = addr >> PAGE_SHIFT; page_offset -= bomapping->mmnode.start; - mutex_lock(&bo->base.pages_lock); + obj = &bo->base.base; + + dma_resv_lock(obj->resv, NULL); if (!bo->base.pages) { bo->sgts = kvmalloc_array(bo->base.base.size / SZ_2M, sizeof(struct sg_table), GFP_KERNEL | __GFP_ZERO); if (!bo->sgts) { - mutex_unlock(&bo->base.pages_lock); ret = -ENOMEM; - goto err_bo; + goto err_unlock; } pages = kvmalloc_array(bo->base.base.size >> PAGE_SHIFT, @@ -481,9 +483,8 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, if (!pages) { kvfree(bo->sgts); bo->sgts = NULL; - mutex_unlock(&bo->base.pages_lock); ret = -ENOMEM; - goto err_bo; + goto err_unlock; } bo->base.pages = pages; bo->base.pages_use_count = 1; @@ -491,7 +492,6 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, pages = bo->base.pages; if (pages[page_offset]) { /* Pages are already mapped, bail out. */ - mutex_unlock(&bo->base.pages_lock); goto out; } } @@ -502,15 +502,12 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, for (i = page_offset; i < page_offset + NUM_FAULT_PAGES; i++) { pages[i] = shmem_read_mapping_page(mapping, i); if (IS_ERR(pages[i])) { - mutex_unlock(&bo->base.pages_lock); ret = PTR_ERR(pages[i]); pages[i] = NULL; goto err_pages; } } - mutex_unlock(&bo->base.pages_lock); - sgt = &bo->sgts[page_offset / (SZ_2M / PAGE_SIZE)]; ret = sg_alloc_table_from_pages(sgt, pages + page_offset, NUM_FAULT_PAGES, 0, SZ_2M, GFP_KERNEL); @@ -529,6 +526,8 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, dev_dbg(pfdev->dev, "mapped page fault @ AS%d %llx", as, addr); out: + dma_resv_unlock(obj->resv); + panfrost_gem_mapping_put(bomapping); return 0; @@ -537,6 +536,8 @@ err_map: sg_free_table(sgt); err_pages: drm_gem_shmem_put_pages(&bo->base); +err_unlock: + dma_resv_unlock(obj->resv); err_bo: panfrost_gem_mapping_put(bomapping); return ret; diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index 43049c8028b2..ba3b5b5f0cdf 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -224,10 +224,7 @@ static const struct drm_driver pl111_drm_driver = { .minor = 0, .patchlevel = 0, .dumb_create = drm_gem_dma_dumb_create, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = pl111_gem_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, #if defined(CONFIG_DEBUG_FS) .debugfs_init = pl111_debugfs_init, diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index a3b83f89e061..b30ede1cf62d 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -290,8 +290,6 @@ static struct drm_driver qxl_driver = { #if defined(CONFIG_DEBUG_FS) .debugfs_init = qxl_debugfs_init, #endif - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = qxl_gem_prime_import_sg_table, .fops = &qxl_fops, .ioctls = qxl_ioctls, diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index e4374814f0ef..39cdede460b5 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -604,10 +604,7 @@ static const struct drm_driver kms_driver = { .dumb_map_offset = radeon_mode_dumb_mmap, .fops = &radeon_driver_kms_fops, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = radeon_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c b/drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c index e2a67dda4658..26a2f5ad8ee5 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c @@ -187,11 +187,9 @@ static int rcar_cmm_probe(struct platform_device *pdev) return 0; } -static int rcar_cmm_remove(struct platform_device *pdev) +static void rcar_cmm_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - - return 0; } static const struct of_device_id rcar_cmm_of_table[] = { @@ -203,7 +201,7 @@ MODULE_DEVICE_TABLE(of, rcar_cmm_of_table); static struct platform_driver rcar_cmm_platform_driver = { .probe = rcar_cmm_probe, - .remove = rcar_cmm_remove, + .remove_new = rcar_cmm_remove, .driver = { .name = "rcar-cmm", .of_match_table = rcar_cmm_of_table, diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c index 1ffde19cb87f..a4f3615f3291 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c @@ -605,10 +605,7 @@ DEFINE_DRM_GEM_DMA_FOPS(rcar_du_fops); static const struct drm_driver rcar_du_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .dumb_create = rcar_du_dumb_create, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = rcar_du_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .fops = &rcar_du_fops, .name = "rcar-du", .desc = "Renesas R-Car Display Unit", @@ -642,7 +639,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(rcar_du_pm_ops, * Platform driver */ -static int rcar_du_remove(struct platform_device *pdev) +static void rcar_du_remove(struct platform_device *pdev) { struct rcar_du_device *rcdu = platform_get_drvdata(pdev); struct drm_device *ddev = &rcdu->ddev; @@ -651,8 +648,6 @@ static int rcar_du_remove(struct platform_device *pdev) drm_atomic_helper_shutdown(ddev); drm_kms_helper_poll_fini(ddev); - - return 0; } static void rcar_du_shutdown(struct platform_device *pdev) @@ -728,7 +723,7 @@ error: static struct platform_driver rcar_du_platform_driver = { .probe = rcar_du_probe, - .remove = rcar_du_remove, + .remove_new = rcar_du_remove, .shutdown = rcar_du_shutdown, .driver = { .name = "rcar-du", diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c index 18ed14911b98..119d69d20b23 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c @@ -93,13 +93,11 @@ static int rcar_dw_hdmi_probe(struct platform_device *pdev) return 0; } -static int rcar_dw_hdmi_remove(struct platform_device *pdev) +static void rcar_dw_hdmi_remove(struct platform_device *pdev) { struct dw_hdmi *hdmi = platform_get_drvdata(pdev); dw_hdmi_remove(hdmi); - - return 0; } static const struct of_device_id rcar_dw_hdmi_of_table[] = { @@ -110,7 +108,7 @@ MODULE_DEVICE_TABLE(of, rcar_dw_hdmi_of_table); static struct platform_driver rcar_dw_hdmi_platform_driver = { .probe = rcar_dw_hdmi_probe, - .remove = rcar_dw_hdmi_remove, + .remove_new = rcar_dw_hdmi_remove, .driver = { .name = "rcar-dw-hdmi", .of_match_table = rcar_dw_hdmi_of_table, diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c index ca215b588fd7..92ba43a6fe38 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c @@ -918,15 +918,13 @@ static int rcar_lvds_probe(struct platform_device *pdev) return 0; } -static int rcar_lvds_remove(struct platform_device *pdev) +static void rcar_lvds_remove(struct platform_device *pdev) { struct rcar_lvds *lvds = platform_get_drvdata(pdev); drm_bridge_remove(&lvds->bridge); pm_runtime_disable(&pdev->dev); - - return 0; } static const struct rcar_lvds_device_info rcar_lvds_gen2_info = { @@ -1020,7 +1018,7 @@ static const struct dev_pm_ops rcar_lvds_pm_ops = { static struct platform_driver rcar_lvds_platform_driver = { .probe = rcar_lvds_probe, - .remove = rcar_lvds_remove, + .remove_new = rcar_lvds_remove, .driver = { .name = "rcar-lvds", .pm = &rcar_lvds_pm_ops, diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c index e10e4d4b89a2..305123a671c6 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c @@ -1044,13 +1044,11 @@ static int rcar_mipi_dsi_probe(struct platform_device *pdev) return 0; } -static int rcar_mipi_dsi_remove(struct platform_device *pdev) +static void rcar_mipi_dsi_remove(struct platform_device *pdev) { struct rcar_mipi_dsi *dsi = platform_get_drvdata(pdev); mipi_dsi_host_unregister(&dsi->host); - - return 0; } static const struct rcar_mipi_dsi_device_info v3u_data = { @@ -1093,7 +1091,7 @@ MODULE_DEVICE_TABLE(of, rcar_mipi_dsi_of_table); static struct platform_driver rcar_mipi_dsi_platform_driver = { .probe = rcar_mipi_dsi_probe, - .remove = rcar_mipi_dsi_remove, + .remove_new = rcar_mipi_dsi_remove, .driver = { .name = "rcar-mipi-dsi", .of_match_table = rcar_mipi_dsi_of_table, diff --git a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c index aa95b85a2964..a97fc4c5d1c8 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c @@ -782,14 +782,12 @@ err_pm_disable: return ret; } -static int rzg2l_mipi_dsi_remove(struct platform_device *pdev) +static void rzg2l_mipi_dsi_remove(struct platform_device *pdev) { struct rzg2l_mipi_dsi *dsi = platform_get_drvdata(pdev); mipi_dsi_host_unregister(&dsi->host); pm_runtime_disable(&pdev->dev); - - return 0; } static const struct of_device_id rzg2l_mipi_dsi_of_table[] = { @@ -801,7 +799,7 @@ MODULE_DEVICE_TABLE(of, rzg2l_mipi_dsi_of_table); static struct platform_driver rzg2l_mipi_dsi_platform_driver = { .probe = rzg2l_mipi_dsi_probe, - .remove = rzg2l_mipi_dsi_remove, + .remove_new = rzg2l_mipi_dsi_remove, .driver = { .name = "rzg2l-mipi-dsi", .pm = &rzg2l_mipi_pm_ops, diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index ad2d3ae7e621..c497382fdb9b 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -419,14 +419,12 @@ err_dp_remove: return ret; } -static int rockchip_dp_remove(struct platform_device *pdev) +static void rockchip_dp_remove(struct platform_device *pdev) { struct rockchip_dp_device *dp = platform_get_drvdata(pdev); component_del(&pdev->dev, &rockchip_dp_component_ops); analogix_dp_remove(dp->adp); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -481,7 +479,7 @@ MODULE_DEVICE_TABLE(of, rockchip_dp_dt_ids); struct platform_driver rockchip_dp_driver = { .probe = rockchip_dp_probe, - .remove = rockchip_dp_remove, + .remove_new = rockchip_dp_remove, .driver = { .name = "rockchip-dp", .pm = &rockchip_dp_pm_ops, diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index b6afe3786b74..a29fbafce393 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -1222,15 +1222,13 @@ static int cdn_dp_probe(struct platform_device *pdev) return component_add(dev, &cdn_dp_component_ops); } -static int cdn_dp_remove(struct platform_device *pdev) +static void cdn_dp_remove(struct platform_device *pdev) { struct cdn_dp_device *dp = platform_get_drvdata(pdev); platform_device_unregister(dp->audio_pdev); cdn_dp_suspend(dp->dev); component_del(&pdev->dev, &cdn_dp_component_ops); - - return 0; } static void cdn_dp_shutdown(struct platform_device *pdev) @@ -1247,7 +1245,7 @@ static const struct dev_pm_ops cdn_dp_pm_ops = { struct platform_driver cdn_dp_driver = { .probe = cdn_dp_probe, - .remove = cdn_dp_remove, + .remove_new = cdn_dp_remove, .shutdown = cdn_dp_shutdown, .driver = { .name = "cdn-dp", diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index 917e79951aac..3e2b1a24b0f1 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -1463,13 +1463,11 @@ static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev) return 0; } -static int dw_mipi_dsi_rockchip_remove(struct platform_device *pdev) +static void dw_mipi_dsi_rockchip_remove(struct platform_device *pdev) { struct dw_mipi_dsi_rockchip *dsi = platform_get_drvdata(pdev); dw_mipi_dsi_remove(dsi->dmd); - - return 0; } static const struct rockchip_dw_dsi_chip_data px30_chip_data[] = { @@ -1671,7 +1669,7 @@ MODULE_DEVICE_TABLE(of, dw_mipi_dsi_rockchip_dt_ids); struct platform_driver dw_mipi_dsi_rockchip_driver = { .probe = dw_mipi_dsi_rockchip_probe, - .remove = dw_mipi_dsi_rockchip_remove, + .remove_new = dw_mipi_dsi_rockchip_remove, .driver = { .of_match_table = dw_mipi_dsi_rockchip_dt_ids, .pm = &dw_mipi_dsi_rockchip_pm_ops, diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 112699949db9..341550199111 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -684,11 +684,9 @@ static int dw_hdmi_rockchip_probe(struct platform_device *pdev) return component_add(&pdev->dev, &dw_hdmi_rockchip_ops); } -static int dw_hdmi_rockchip_remove(struct platform_device *pdev) +static void dw_hdmi_rockchip_remove(struct platform_device *pdev) { component_del(&pdev->dev, &dw_hdmi_rockchip_ops); - - return 0; } static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev) @@ -706,7 +704,7 @@ static const struct dev_pm_ops dw_hdmi_rockchip_pm = { struct platform_driver dw_hdmi_rockchip_pltfm_driver = { .probe = dw_hdmi_rockchip_probe, - .remove = dw_hdmi_rockchip_remove, + .remove_new = dw_hdmi_rockchip_remove, .driver = { .name = "dwhdmi-rockchip", .pm = &dw_hdmi_rockchip_pm, diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c index 9afb889963c1..22999eac6df5 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c @@ -919,11 +919,9 @@ static int inno_hdmi_probe(struct platform_device *pdev) return component_add(&pdev->dev, &inno_hdmi_ops); } -static int inno_hdmi_remove(struct platform_device *pdev) +static void inno_hdmi_remove(struct platform_device *pdev) { component_del(&pdev->dev, &inno_hdmi_ops); - - return 0; } static const struct of_device_id inno_hdmi_dt_ids[] = { @@ -935,7 +933,7 @@ MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids); struct platform_driver inno_hdmi_driver = { .probe = inno_hdmi_probe, - .remove = inno_hdmi_remove, + .remove_new = inno_hdmi_remove, .driver = { .name = "innohdmi-rockchip", .of_match_table = inno_hdmi_dt_ids, diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c index b5d042ee052f..fa6e592e0276 100644 --- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -858,11 +858,9 @@ static int rk3066_hdmi_probe(struct platform_device *pdev) return component_add(&pdev->dev, &rk3066_hdmi_ops); } -static int rk3066_hdmi_remove(struct platform_device *pdev) +static void rk3066_hdmi_remove(struct platform_device *pdev) { component_del(&pdev->dev, &rk3066_hdmi_ops); - - return 0; } static const struct of_device_id rk3066_hdmi_dt_ids[] = { @@ -873,7 +871,7 @@ MODULE_DEVICE_TABLE(of, rk3066_hdmi_dt_ids); struct platform_driver rk3066_hdmi_driver = { .probe = rk3066_hdmi_probe, - .remove = rk3066_hdmi_remove, + .remove_new = rk3066_hdmi_remove, .driver = { .name = "rockchip-rk3066-hdmi", .of_match_table = rk3066_hdmi_dt_ids, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index d97f2edc646b..e35be6ea2849 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -224,10 +224,7 @@ DEFINE_DRM_GEM_FOPS(rockchip_drm_driver_fops); static const struct drm_driver rockchip_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, .dumb_create = rockchip_gem_dumb_create, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .fops = &rockchip_drm_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -450,13 +447,11 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev) return 0; } -static int rockchip_drm_platform_remove(struct platform_device *pdev) +static void rockchip_drm_platform_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &rockchip_drm_ops); rockchip_drm_match_remove(&pdev->dev); - - return 0; } static void rockchip_drm_platform_shutdown(struct platform_device *pdev) @@ -475,7 +470,7 @@ MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids); static struct platform_driver rockchip_drm_platform_driver = { .probe = rockchip_drm_platform_probe, - .remove = rockchip_drm_platform_remove, + .remove_new = rockchip_drm_platform_remove, .shutdown = rockchip_drm_platform_shutdown, .driver = { .name = "rockchip-drm", diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index 68f6ebb33460..582859387792 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -739,19 +739,17 @@ static int rockchip_lvds_probe(struct platform_device *pdev) return ret; } -static int rockchip_lvds_remove(struct platform_device *pdev) +static void rockchip_lvds_remove(struct platform_device *pdev) { struct rockchip_lvds *lvds = platform_get_drvdata(pdev); component_del(&pdev->dev, &rockchip_lvds_component_ops); clk_unprepare(lvds->pclk); - - return 0; } struct platform_driver rockchip_lvds_driver = { .probe = rockchip_lvds_probe, - .remove = rockchip_lvds_remove, + .remove_new = rockchip_lvds_remove, .driver = { .name = "rockchip-lvds", .of_match_table = of_match_ptr(rockchip_lvds_dt_ids), diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 9d30aa73b542..62b573f282a7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -264,16 +264,14 @@ static int vop2_probe(struct platform_device *pdev) return component_add(dev, &vop2_component_ops); } -static int vop2_remove(struct platform_device *pdev) +static void vop2_remove(struct platform_device *pdev) { component_del(&pdev->dev, &vop2_component_ops); - - return 0; } struct platform_driver vop2_platform_driver = { .probe = vop2_probe, - .remove = vop2_remove, + .remove_new = vop2_remove, .driver = { .name = "rockchip-vop2", .of_match_table = of_match_ptr(vop2_dt_match), diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 20ac7811c5eb..7b2805006776 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -1163,16 +1163,14 @@ static int vop_probe(struct platform_device *pdev) return component_add(dev, &vop_component_ops); } -static int vop_remove(struct platform_device *pdev) +static void vop_remove(struct platform_device *pdev) { component_del(&pdev->dev, &vop_component_ops); - - return 0; } struct platform_driver vop_platform_driver = { .probe = vop_probe, - .remove = vop_remove, + .remove_new = vop_remove, .driver = { .name = "rockchip-vop", .of_match_table = vop_driver_dt_match, diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 8cbf5aa66e19..b3dc1ca9dc10 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -99,29 +99,44 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = { .default_vcomh = 0x40, .default_dclk_div = 1, .default_dclk_frq = 5, + .default_width = 132, + .default_height = 64, .page_mode_only = 1, + .page_height = 8, }, [SSD1305_ID] = { .default_vcomh = 0x34, .default_dclk_div = 1, .default_dclk_frq = 7, + .default_width = 132, + .default_height = 64, + .page_height = 8, }, [SSD1306_ID] = { .default_vcomh = 0x20, .default_dclk_div = 1, .default_dclk_frq = 8, .need_chargepump = 1, + .default_width = 128, + .default_height = 64, + .page_height = 8, }, [SSD1307_ID] = { .default_vcomh = 0x20, .default_dclk_div = 2, .default_dclk_frq = 12, .need_pwm = 1, + .default_width = 128, + .default_height = 39, + .page_height = 8, }, [SSD1309_ID] = { .default_vcomh = 0x34, .default_dclk_div = 1, .default_dclk_frq = 10, + .default_width = 128, + .default_height = 64, + .page_height = 8, } }; EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X); @@ -131,6 +146,38 @@ static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm) return container_of(drm, struct ssd130x_device, drm); } +static int ssd130x_buf_alloc(struct ssd130x_device *ssd130x) +{ + unsigned int page_height = ssd130x->device_info->page_height; + unsigned int pages = DIV_ROUND_UP(ssd130x->height, page_height); + const struct drm_format_info *fi; + unsigned int pitch; + + fi = drm_format_info(DRM_FORMAT_C1); + if (!fi) + return -EINVAL; + + pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width); + + ssd130x->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL); + if (!ssd130x->buffer) + return -ENOMEM; + + ssd130x->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL); + if (!ssd130x->data_array) { + kfree(ssd130x->buffer); + return -ENOMEM; + } + + return 0; +} + +static void ssd130x_buf_free(struct ssd130x_device *ssd130x) +{ + kfree(ssd130x->data_array); + kfree(ssd130x->buffer); +} + /* * Helper to write data (SSD130X_DATA) to the device. */ @@ -419,26 +466,23 @@ static int ssd130x_init(struct ssd130x_device *ssd130x) SSD130X_SET_ADDRESS_MODE_HORIZONTAL); } -static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf, - struct drm_rect *rect) +static int ssd130x_update_rect(struct ssd130x_device *ssd130x, struct drm_rect *rect) { unsigned int x = rect->x1; unsigned int y = rect->y1; + u8 *buf = ssd130x->buffer; + u8 *data_array = ssd130x->data_array; unsigned int width = drm_rect_width(rect); unsigned int height = drm_rect_height(rect); unsigned int line_length = DIV_ROUND_UP(width, 8); - unsigned int pages = DIV_ROUND_UP(height, 8); + unsigned int page_height = ssd130x->device_info->page_height; + unsigned int pages = DIV_ROUND_UP(height, page_height); struct drm_device *drm = &ssd130x->drm; u32 array_idx = 0; int ret, i, j, k; - u8 *data_array = NULL; drm_WARN_ONCE(drm, y % 8 != 0, "y must be aligned to screen page\n"); - data_array = kcalloc(width, pages, GFP_KERNEL); - if (!data_array) - return -ENOMEM; - /* * The screen is divided in pages, each having a height of 8 * pixels, and the width of the screen. When sending a byte of @@ -472,11 +516,11 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf, /* Set address range for horizontal addressing mode */ ret = ssd130x_set_col_range(ssd130x, ssd130x->col_offset + x, width); if (ret < 0) - goto out_free; + return ret; ret = ssd130x_set_page_range(ssd130x, ssd130x->page_offset + y / 8, pages); if (ret < 0) - goto out_free; + return ret; } for (i = 0; i < pages; i++) { @@ -506,11 +550,11 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf, ssd130x->page_offset + i, ssd130x->col_offset + x); if (ret < 0) - goto out_free; + return ret; ret = ssd130x_write_data(ssd130x, data_array, width); if (ret < 0) - goto out_free; + return ret; array_idx = 0; } @@ -520,14 +564,11 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf, if (!ssd130x->page_address_mode) ret = ssd130x_write_data(ssd130x, data_array, width * pages); -out_free: - kfree(data_array); return ret; } static void ssd130x_clear_screen(struct ssd130x_device *ssd130x) { - u8 *buf = NULL; struct drm_rect fullscreen = { .x1 = 0, .x2 = ssd130x->width, @@ -535,47 +576,38 @@ static void ssd130x_clear_screen(struct ssd130x_device *ssd130x) .y2 = ssd130x->height, }; - buf = kcalloc(DIV_ROUND_UP(ssd130x->width, 8), ssd130x->height, - GFP_KERNEL); - if (!buf) - return; - - ssd130x_update_rect(ssd130x, buf, &fullscreen); - - kfree(buf); + ssd130x_update_rect(ssd130x, &fullscreen); } static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *vmap, struct drm_rect *rect) { struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev); + unsigned int page_height = ssd130x->device_info->page_height; struct iosys_map dst; unsigned int dst_pitch; int ret = 0; - u8 *buf = NULL; + u8 *buf = ssd130x->buffer; + + if (!buf) + return 0; /* Align y to display page boundaries */ - rect->y1 = round_down(rect->y1, 8); - rect->y2 = min_t(unsigned int, round_up(rect->y2, 8), ssd130x->height); + rect->y1 = round_down(rect->y1, page_height); + rect->y2 = min_t(unsigned int, round_up(rect->y2, page_height), ssd130x->height); - dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8); - buf = kcalloc(dst_pitch, drm_rect_height(rect), GFP_KERNEL); - if (!buf) - return -ENOMEM; + dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), page_height); ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); if (ret) - goto out_free; + return ret; iosys_map_set_vaddr(&dst, buf); drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect); drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); - ssd130x_update_rect(ssd130x, buf, rect); - -out_free: - kfree(buf); + ssd130x_update_rect(ssd130x, rect); return ret; } @@ -684,14 +716,22 @@ static void ssd130x_encoder_helper_atomic_enable(struct drm_encoder *encoder, return; ret = ssd130x_init(ssd130x); - if (ret) { - ssd130x_power_off(ssd130x); - return; - } + if (ret) + goto power_off; + + ret = ssd130x_buf_alloc(ssd130x); + if (ret) + goto power_off; ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON); backlight_enable(ssd130x->bl_dev); + + return; + +power_off: + ssd130x_power_off(ssd130x); + return; } static void ssd130x_encoder_helper_atomic_disable(struct drm_encoder *encoder, @@ -704,6 +744,8 @@ static void ssd130x_encoder_helper_atomic_disable(struct drm_encoder *encoder, ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF); + ssd130x_buf_free(ssd130x); + ssd130x_power_off(ssd130x); } @@ -798,10 +840,10 @@ static void ssd130x_parse_properties(struct ssd130x_device *ssd130x) struct device *dev = ssd130x->dev; if (device_property_read_u32(dev, "solomon,width", &ssd130x->width)) - ssd130x->width = 96; + ssd130x->width = ssd130x->device_info->default_width; if (device_property_read_u32(dev, "solomon,height", &ssd130x->height)) - ssd130x->height = 16; + ssd130x->height = ssd130x->device_info->default_height; if (device_property_read_u32(dev, "solomon,page-offset", &ssd130x->page_offset)) ssd130x->page_offset = 1; diff --git a/drivers/gpu/drm/solomon/ssd130x.h b/drivers/gpu/drm/solomon/ssd130x.h index db03ee5db392..161588b1cc4d 100644 --- a/drivers/gpu/drm/solomon/ssd130x.h +++ b/drivers/gpu/drm/solomon/ssd130x.h @@ -37,6 +37,9 @@ struct ssd130x_deviceinfo { u32 default_vcomh; u32 default_dclk_div; u32 default_dclk_frq; + u32 default_width; + u32 default_height; + u32 page_height; int need_pwm; int need_chargepump; bool page_mode_only; @@ -86,6 +89,9 @@ struct ssd130x_device { u8 col_end; u8 page_start; u8 page_end; + + u8 *buffer; + u8 *data_array; }; extern const struct ssd130x_deviceinfo ssd130x_variants[]; diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c index 142a8e1b4436..33487a1fed8f 100644 --- a/drivers/gpu/drm/sti/sti_compositor.c +++ b/drivers/gpu/drm/sti/sti_compositor.c @@ -258,10 +258,9 @@ static int sti_compositor_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sti_compositor_ops); } -static int sti_compositor_remove(struct platform_device *pdev) +static void sti_compositor_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sti_compositor_ops); - return 0; } struct platform_driver sti_compositor_driver = { @@ -270,7 +269,7 @@ struct platform_driver sti_compositor_driver = { .of_match_table = compositor_of_match, }, .probe = sti_compositor_probe, - .remove = sti_compositor_remove, + .remove_new = sti_compositor_remove, }; MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 1b87b5899f9e..346c151e87be 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -246,11 +246,9 @@ static int sti_platform_probe(struct platform_device *pdev) return component_master_add_with_match(dev, &sti_ops, match); } -static int sti_platform_remove(struct platform_device *pdev) +static void sti_platform_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &sti_ops); - - return 0; } static const struct of_device_id sti_dt_ids[] = { @@ -261,7 +259,7 @@ MODULE_DEVICE_TABLE(of, sti_dt_ids); static struct platform_driver sti_platform_driver = { .probe = sti_platform_probe, - .remove = sti_platform_remove, + .remove_new = sti_platform_remove, .driver = { .name = DRIVER_NAME, .of_match_table = sti_dt_ids, diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index 0c6679e361c8..fd1df4ce3852 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -567,10 +567,9 @@ static int sti_dvo_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sti_dvo_ops); } -static int sti_dvo_remove(struct platform_device *pdev) +static void sti_dvo_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sti_dvo_ops); - return 0; } static const struct of_device_id dvo_of_match[] = { @@ -586,7 +585,7 @@ struct platform_driver sti_dvo_driver = { .of_match_table = dvo_of_match, }, .probe = sti_dvo_probe, - .remove = sti_dvo_remove, + .remove_new = sti_dvo_remove, }; MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 15097ac67931..6ee35612a14e 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -792,10 +792,9 @@ static int sti_hda_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sti_hda_ops); } -static int sti_hda_remove(struct platform_device *pdev) +static void sti_hda_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sti_hda_ops); - return 0; } static const struct of_device_id hda_of_match[] = { @@ -812,7 +811,7 @@ struct platform_driver sti_hda_driver = { .of_match_table = hda_of_match, }, .probe = sti_hda_probe, - .remove = sti_hda_remove, + .remove_new = sti_hda_remove, }; MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index dc1562f14ceb..500936d5743c 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -1472,7 +1472,7 @@ static int sti_hdmi_probe(struct platform_device *pdev) return ret; } -static int sti_hdmi_remove(struct platform_device *pdev) +static void sti_hdmi_remove(struct platform_device *pdev) { struct sti_hdmi *hdmi = dev_get_drvdata(&pdev->dev); @@ -1480,8 +1480,6 @@ static int sti_hdmi_remove(struct platform_device *pdev) if (hdmi->audio_pdev) platform_device_unregister(hdmi->audio_pdev); component_del(&pdev->dev, &sti_hdmi_ops); - - return 0; } struct platform_driver sti_hdmi_driver = { @@ -1491,7 +1489,7 @@ struct platform_driver sti_hdmi_driver = { .of_match_table = hdmi_of_match, }, .probe = sti_hdmi_probe, - .remove = sti_hdmi_remove, + .remove_new = sti_hdmi_remove, }; MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c index 02b77279f6e4..0fb48ac044d8 100644 --- a/drivers/gpu/drm/sti/sti_hqvdp.c +++ b/drivers/gpu/drm/sti/sti_hqvdp.c @@ -1400,10 +1400,9 @@ static int sti_hqvdp_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sti_hqvdp_ops); } -static int sti_hqvdp_remove(struct platform_device *pdev) +static void sti_hqvdp_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sti_hqvdp_ops); - return 0; } static const struct of_device_id hqvdp_of_match[] = { @@ -1419,7 +1418,7 @@ struct platform_driver sti_hqvdp_driver = { .of_match_table = hqvdp_of_match, }, .probe = sti_hqvdp_probe, - .remove = sti_hqvdp_remove, + .remove_new = sti_hqvdp_remove, }; MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c index 2499715a69b7..64615638b79a 100644 --- a/drivers/gpu/drm/sti/sti_tvout.c +++ b/drivers/gpu/drm/sti/sti_tvout.c @@ -872,10 +872,9 @@ static int sti_tvout_probe(struct platform_device *pdev) return component_add(dev, &sti_tvout_ops); } -static int sti_tvout_remove(struct platform_device *pdev) +static void sti_tvout_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sti_tvout_ops); - return 0; } static const struct of_device_id tvout_of_match[] = { @@ -891,7 +890,7 @@ struct platform_driver sti_tvout_driver = { .of_match_table = tvout_of_match, }, .probe = sti_tvout_probe, - .remove = sti_tvout_remove, + .remove_new = sti_tvout_remove, }; MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index cb4404b3ce62..c387fb5a87c3 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -213,7 +213,7 @@ err_put: return ret; } -static int stm_drm_platform_remove(struct platform_device *pdev) +static void stm_drm_platform_remove(struct platform_device *pdev) { struct drm_device *ddev = platform_get_drvdata(pdev); @@ -222,8 +222,6 @@ static int stm_drm_platform_remove(struct platform_device *pdev) drm_dev_unregister(ddev); drv_unload(ddev); drm_dev_put(ddev); - - return 0; } static const struct of_device_id drv_dt_ids[] = { @@ -234,7 +232,7 @@ MODULE_DEVICE_TABLE(of, drv_dt_ids); static struct platform_driver stm_drm_platform_driver = { .probe = stm_drm_platform_probe, - .remove = stm_drm_platform_remove, + .remove_new = stm_drm_platform_remove, .driver = { .name = "stm32-display", .of_match_table = drv_dt_ids, diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c index 1750b6a25e87..d5f8c923d7bc 100644 --- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c @@ -535,15 +535,13 @@ err_clk_get: return ret; } -static int dw_mipi_dsi_stm_remove(struct platform_device *pdev) +static void dw_mipi_dsi_stm_remove(struct platform_device *pdev) { struct dw_mipi_dsi_stm *dsi = platform_get_drvdata(pdev); dw_mipi_dsi_remove(dsi->dsi); clk_disable_unprepare(dsi->pllref_clk); regulator_disable(dsi->vdd_supply); - - return 0; } static int __maybe_unused dw_mipi_dsi_stm_suspend(struct device *dev) @@ -588,7 +586,7 @@ static const struct dev_pm_ops dw_mipi_dsi_stm_pm_ops = { static struct platform_driver dw_mipi_dsi_stm_driver = { .probe = dw_mipi_dsi_stm_probe, - .remove = dw_mipi_dsi_stm_remove, + .remove_new = dw_mipi_dsi_stm_remove, .driver = { .of_match_table = dw_mipi_dsi_stm_dt_ids, .name = "stm32-display-dsi", diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index b11dbd50d73e..335fd0edb904 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -965,11 +965,9 @@ static int sun4i_backend_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sun4i_backend_ops); } -static int sun4i_backend_remove(struct platform_device *pdev) +static void sun4i_backend_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sun4i_backend_ops); - - return 0; } static const struct sun4i_backend_quirks sun4i_backend_quirks = { @@ -1028,7 +1026,7 @@ MODULE_DEVICE_TABLE(of, sun4i_backend_of_table); static struct platform_driver sun4i_backend_platform_driver = { .probe = sun4i_backend_probe, - .remove = sun4i_backend_remove, + .remove_new = sun4i_backend_remove, .driver = { .name = "sun4i-backend", .of_match_table = sun4i_backend_of_table, diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index daa7faf72a4b..6a8dfc022d3c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -408,11 +408,9 @@ static int sun4i_drv_probe(struct platform_device *pdev) return 0; } -static int sun4i_drv_remove(struct platform_device *pdev) +static void sun4i_drv_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &sun4i_drv_master_ops); - - return 0; } static const struct of_device_id sun4i_drv_of_table[] = { @@ -438,7 +436,7 @@ MODULE_DEVICE_TABLE(of, sun4i_drv_of_table); static struct platform_driver sun4i_drv_platform_driver = { .probe = sun4i_drv_probe, - .remove = sun4i_drv_remove, + .remove_new = sun4i_drv_remove, .driver = { .name = "sun4i-drm", .of_match_table = sun4i_drv_of_table, diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c b/drivers/gpu/drm/sun4i/sun4i_frontend.c index 799ab7460ae5..3872c91a85c7 100644 --- a/drivers/gpu/drm/sun4i/sun4i_frontend.c +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c @@ -634,11 +634,9 @@ static int sun4i_frontend_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sun4i_frontend_ops); } -static int sun4i_frontend_remove(struct platform_device *pdev) +static void sun4i_frontend_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sun4i_frontend_ops); - - return 0; } static int sun4i_frontend_runtime_resume(struct device *dev) @@ -719,7 +717,7 @@ MODULE_DEVICE_TABLE(of, sun4i_frontend_of_table); static struct platform_driver sun4i_frontend_driver = { .probe = sun4i_frontend_probe, - .remove = sun4i_frontend_remove, + .remove_new = sun4i_frontend_remove, .driver = { .name = "sun4i-frontend", .of_match_table = sun4i_frontend_of_table, diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index c0df5e892fa7..c139e1262e03 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -693,11 +693,9 @@ static int sun4i_hdmi_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sun4i_hdmi_ops); } -static int sun4i_hdmi_remove(struct platform_device *pdev) +static void sun4i_hdmi_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sun4i_hdmi_ops); - - return 0; } static const struct of_device_id sun4i_hdmi_of_table[] = { @@ -710,7 +708,7 @@ MODULE_DEVICE_TABLE(of, sun4i_hdmi_of_table); static struct platform_driver sun4i_hdmi_driver = { .probe = sun4i_hdmi_probe, - .remove = sun4i_hdmi_remove, + .remove_new = sun4i_hdmi_remove, .driver = { .name = "sun4i-hdmi", .of_match_table = sun4i_hdmi_of_table, diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 6a52fb12cbfb..b07ccee78cc1 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -1331,11 +1331,9 @@ static int sun4i_tcon_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sun4i_tcon_ops); } -static int sun4i_tcon_remove(struct platform_device *pdev) +static void sun4i_tcon_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sun4i_tcon_ops); - - return 0; } /* platform specific TCON muxing callbacks */ @@ -1570,7 +1568,7 @@ EXPORT_SYMBOL(sun4i_tcon_of_table); static struct platform_driver sun4i_tcon_platform_driver = { .probe = sun4i_tcon_probe, - .remove = sun4i_tcon_remove, + .remove_new = sun4i_tcon_remove, .driver = { .name = "sun4i-tcon", .of_match_table = sun4i_tcon_of_table, diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c index 9625a00a48ba..ec65d9d59de7 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tv.c +++ b/drivers/gpu/drm/sun4i/sun4i_tv.c @@ -546,11 +546,9 @@ static int sun4i_tv_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sun4i_tv_ops); } -static int sun4i_tv_remove(struct platform_device *pdev) +static void sun4i_tv_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sun4i_tv_ops); - - return 0; } static const struct of_device_id sun4i_tv_of_table[] = { @@ -561,7 +559,7 @@ MODULE_DEVICE_TABLE(of, sun4i_tv_of_table); static struct platform_driver sun4i_tv_platform_driver = { .probe = sun4i_tv_probe, - .remove = sun4i_tv_remove, + .remove_new = sun4i_tv_remove, .driver = { .name = "sun4i-tve", .of_match_table = sun4i_tv_of_table, diff --git a/drivers/gpu/drm/sun4i/sun6i_drc.c b/drivers/gpu/drm/sun4i/sun6i_drc.c index 4fbe9a6b5182..0d342f43fa93 100644 --- a/drivers/gpu/drm/sun4i/sun6i_drc.c +++ b/drivers/gpu/drm/sun4i/sun6i_drc.c @@ -95,11 +95,9 @@ static int sun6i_drc_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sun6i_drc_ops); } -static int sun6i_drc_remove(struct platform_device *pdev) +static void sun6i_drc_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sun6i_drc_ops); - - return 0; } static const struct of_device_id sun6i_drc_of_table[] = { @@ -114,7 +112,7 @@ MODULE_DEVICE_TABLE(of, sun6i_drc_of_table); static struct platform_driver sun6i_drc_platform_driver = { .probe = sun6i_drc_probe, - .remove = sun6i_drc_remove, + .remove_new = sun6i_drc_remove, .driver = { .name = "sun6i-drc", .of_match_table = sun6i_drc_of_table, diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c index 760ff05eabf4..4abf4f102007 100644 --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c @@ -1200,7 +1200,7 @@ err_attach_clk: return ret; } -static int sun6i_dsi_remove(struct platform_device *pdev) +static void sun6i_dsi_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct sun6i_dsi *dsi = dev_get_drvdata(dev); @@ -1211,8 +1211,6 @@ static int sun6i_dsi_remove(struct platform_device *pdev) clk_rate_exclusive_put(dsi->mod_clk); regmap_mmio_detach_clk(dsi->regs); - - return 0; } static const struct sun6i_dsi_variant sun6i_a31_mipi_dsi_variant = { @@ -1246,7 +1244,7 @@ MODULE_DEVICE_TABLE(of, sun6i_dsi_of_table); static struct platform_driver sun6i_dsi_platform_driver = { .probe = sun6i_dsi_probe, - .remove = sun6i_dsi_remove, + .remove_new = sun6i_dsi_remove, .driver = { .name = "sun6i-mipi-dsi", .of_match_table = sun6i_dsi_of_table, diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c index 7cab4213a680..26dd8f0dde99 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c @@ -235,11 +235,9 @@ static int sun8i_dw_hdmi_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sun8i_dw_hdmi_ops); } -static int sun8i_dw_hdmi_remove(struct platform_device *pdev) +static void sun8i_dw_hdmi_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sun8i_dw_hdmi_ops); - - return 0; } static const struct sun8i_dw_hdmi_quirks sun8i_a83t_quirks = { @@ -266,7 +264,7 @@ MODULE_DEVICE_TABLE(of, sun8i_dw_hdmi_dt_ids); static struct platform_driver sun8i_dw_hdmi_pltfm_driver = { .probe = sun8i_dw_hdmi_probe, - .remove = sun8i_dw_hdmi_remove, + .remove_new = sun8i_dw_hdmi_remove, .driver = { .name = "sun8i-dw-hdmi", .of_match_table = sun8i_dw_hdmi_dt_ids, diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index 11d5244a5aa5..07a638106414 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -555,11 +555,9 @@ static int sun8i_mixer_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sun8i_mixer_ops); } -static int sun8i_mixer_remove(struct platform_device *pdev) +static void sun8i_mixer_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sun8i_mixer_ops); - - return 0; } static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { @@ -711,7 +709,7 @@ MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); static struct platform_driver sun8i_mixer_platform_driver = { .probe = sun8i_mixer_probe, - .remove = sun8i_mixer_remove, + .remove_new = sun8i_mixer_remove, .driver = { .name = "sun8i-mixer", .of_match_table = sun8i_mixer_of_table, diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c index da97682b6835..2e8dd7723e29 100644 --- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c +++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c @@ -261,11 +261,9 @@ static int sun8i_tcon_top_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sun8i_tcon_top_ops); } -static int sun8i_tcon_top_remove(struct platform_device *pdev) +static void sun8i_tcon_top_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sun8i_tcon_top_ops); - - return 0; } static const struct sun8i_tcon_top_quirks sun8i_r40_tcon_top_quirks = { @@ -302,7 +300,7 @@ EXPORT_SYMBOL(sun8i_tcon_top_of_table); static struct platform_driver sun8i_tcon_top_platform_driver = { .probe = sun8i_tcon_top_probe, - .remove = sun8i_tcon_top_remove, + .remove_new = sun8i_tcon_top_remove, .driver = { .name = "sun8i-tcon-top", .of_match_table = sun8i_tcon_top_of_table, diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 35ff303c6674..ff36171c8fb7 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -887,8 +887,6 @@ static const struct drm_driver tegra_drm_driver = { .debugfs_init = tegra_debugfs_init, #endif - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = tegra_gem_prime_import, .dumb_create = tegra_bo_dumb_create, diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index dea38892d6e6..a4023163493d 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -694,8 +694,6 @@ static int tegra_gem_prime_mmap(struct dma_buf *buf, struct vm_area_struct *vma) struct drm_gem_object *gem = buf->priv; int err; - dma_resv_assert_held(buf->resv); - err = drm_gem_mmap_obj(gem, gem->size, vma); if (err < 0) return err; diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index bca726a8f483..ba7baa622675 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_modes_test.o \ drm_plane_helper_test.o \ drm_probe_helper_test.o \ - drm_rect_test.o + drm_rect_test.o \ + drm_exec_test.o CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN) diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c new file mode 100644 index 000000000000..727ac267682e --- /dev/null +++ b/drivers/gpu/drm/tests/drm_exec_test.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2022 Advanced Micro Devices, Inc. + */ + +#define pr_fmt(fmt) "drm_exec: " fmt + +#include <kunit/test.h> + +#include <linux/module.h> +#include <linux/prime_numbers.h> + +#include <drm/drm_exec.h> +#include <drm/drm_device.h> +#include <drm/drm_gem.h> + +#include "../lib/drm_random.h" + +static struct drm_device dev; + +static void sanitycheck(struct kunit *test) +{ + struct drm_exec exec; + + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); + drm_exec_fini(&exec); + KUNIT_SUCCEED(test); +} + +static void test_lock(struct kunit *test) +{ + struct drm_gem_object gobj = { }; + struct drm_exec exec; + int ret; + + drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE); + + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); + drm_exec_until_all_locked(&exec) { + ret = drm_exec_lock_obj(&exec, &gobj); + drm_exec_retry_on_contention(&exec); + KUNIT_EXPECT_EQ(test, ret, 0); + if (ret) + break; + } + drm_exec_fini(&exec); +} + +static void test_lock_unlock(struct kunit *test) +{ + struct drm_gem_object gobj = { }; + struct drm_exec exec; + int ret; + + drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE); + + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); + drm_exec_until_all_locked(&exec) { + ret = drm_exec_lock_obj(&exec, &gobj); + drm_exec_retry_on_contention(&exec); + KUNIT_EXPECT_EQ(test, ret, 0); + if (ret) + break; + + drm_exec_unlock_obj(&exec, &gobj); + ret = drm_exec_lock_obj(&exec, &gobj); + drm_exec_retry_on_contention(&exec); + KUNIT_EXPECT_EQ(test, ret, 0); + if (ret) + break; + } + drm_exec_fini(&exec); +} + +static void test_duplicates(struct kunit *test) +{ + struct drm_gem_object gobj = { }; + struct drm_exec exec; + int ret; + + drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE); + + drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES); + drm_exec_until_all_locked(&exec) { + ret = drm_exec_lock_obj(&exec, &gobj); + drm_exec_retry_on_contention(&exec); + KUNIT_EXPECT_EQ(test, ret, 0); + if (ret) + break; + + ret = drm_exec_lock_obj(&exec, &gobj); + drm_exec_retry_on_contention(&exec); + KUNIT_EXPECT_EQ(test, ret, 0); + if (ret) + break; + } + drm_exec_unlock_obj(&exec, &gobj); + drm_exec_fini(&exec); +} + + + +static void test_prepare(struct kunit *test) +{ + struct drm_gem_object gobj = { }; + struct drm_exec exec; + int ret; + + drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE); + + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); + drm_exec_until_all_locked(&exec) { + ret = drm_exec_prepare_obj(&exec, &gobj, 1); + drm_exec_retry_on_contention(&exec); + KUNIT_EXPECT_EQ(test, ret, 0); + if (ret) + break; + } + drm_exec_fini(&exec); +} + +static void test_prepare_array(struct kunit *test) +{ + struct drm_gem_object gobj1 = { }; + struct drm_gem_object gobj2 = { }; + struct drm_gem_object *array[] = { &gobj1, &gobj2 }; + struct drm_exec exec; + int ret; + + drm_gem_private_object_init(&dev, &gobj1, PAGE_SIZE); + drm_gem_private_object_init(&dev, &gobj2, PAGE_SIZE); + + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); + drm_exec_until_all_locked(&exec) + ret = drm_exec_prepare_array(&exec, array, ARRAY_SIZE(array), + 1); + KUNIT_EXPECT_EQ(test, ret, 0); + drm_exec_fini(&exec); +} + +static struct kunit_case drm_exec_tests[] = { + KUNIT_CASE(sanitycheck), + KUNIT_CASE(test_lock), + KUNIT_CASE(test_lock_unlock), + KUNIT_CASE(test_duplicates), + KUNIT_CASE(test_prepare), + KUNIT_CASE(test_prepare_array), + {} +}; + +static struct kunit_suite drm_exec_test_suite = { + .name = "drm_exec", + .test_cases = drm_exec_tests, +}; + +kunit_test_suite(drm_exec_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/tests/drm_framebuffer_test.c b/drivers/gpu/drm/tests/drm_framebuffer_test.c index df235b7fdaa5..f759d9f3b76e 100644 --- a/drivers/gpu/drm/tests/drm_framebuffer_test.c +++ b/drivers/gpu/drm/tests/drm_framebuffer_test.c @@ -178,13 +178,13 @@ static const struct drm_framebuffer_test drm_framebuffer_create_cases[] = { .handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 }, } }, -{ .buffer_created = 1, .name = "YVU420 Normal sizes", +{ .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier", .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, .pitches = { 600, 300, 300 }, } }, -{ .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier", +{ .buffer_created = 1, .name = "YVU420 Normal sizes", .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, .handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 }, } diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index dca077411f77..484da1aa27bb 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -275,6 +275,55 @@ const struct dispc_features dispc_j721e_feats = { .vid_order = { 1, 3, 0, 2 }, }; +const struct dispc_features dispc_am625_feats = { + .max_pclk_khz = { + [DISPC_VP_DPI] = 165000, + [DISPC_VP_INTERNAL] = 170000, + }, + + .scaling = { + .in_width_max_5tap_rgb = 1280, + .in_width_max_3tap_rgb = 2560, + .in_width_max_5tap_yuv = 2560, + .in_width_max_3tap_yuv = 4096, + .upscale_limit = 16, + .downscale_limit_5tap = 4, + .downscale_limit_3tap = 2, + /* + * The max supported pixel inc value is 255. The value + * of pixel inc is calculated like this: 1+(xinc-1)*bpp. + * The maximum bpp of all formats supported by the HW + * is 8. So the maximum supported xinc value is 32, + * because 1+(32-1)*8 < 255 < 1+(33-1)*4. + */ + .xinc_max = 32, + }, + + .subrev = DISPC_AM625, + + .common = "common", + .common_regs = tidss_am65x_common_regs, + + .num_vps = 2, + .vp_name = { "vp1", "vp2" }, + .ovr_name = { "ovr1", "ovr2" }, + .vpclk_name = { "vp1", "vp2" }, + .vp_bus_type = { DISPC_VP_INTERNAL, DISPC_VP_DPI }, + + .vp_feat = { .color = { + .has_ctm = true, + .gamma_size = 256, + .gamma_type = TIDSS_GAMMA_8BIT, + }, + }, + + .num_planes = 2, + /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ + .vid_name = { "vid", "vidl1" }, + .vid_lite = { false, true, }, + .vid_order = { 1, 0 }, +}; + static const u16 *dispc_common_regmap; struct dss_vp_data { @@ -776,6 +825,7 @@ dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc) switch (dispc->feat->subrev) { case DISPC_K2G: return dispc_k2g_read_and_clear_irqstatus(dispc); + case DISPC_AM625: case DISPC_AM65X: case DISPC_J721E: return dispc_k3_read_and_clear_irqstatus(dispc); @@ -791,6 +841,7 @@ void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask) case DISPC_K2G: dispc_k2g_set_irqenable(dispc, mask); break; + case DISPC_AM625: case DISPC_AM65X: case DISPC_J721E: dispc_k3_set_irqenable(dispc, mask); @@ -1281,6 +1332,7 @@ void dispc_ovr_set_plane(struct dispc_device *dispc, u32 hw_plane, dispc_k2g_ovr_set_plane(dispc, hw_plane, hw_videoport, x, y, layer); break; + case DISPC_AM625: case DISPC_AM65X: dispc_am65x_ovr_set_plane(dispc, hw_plane, hw_videoport, x, y, layer); @@ -2199,6 +2251,7 @@ static void dispc_plane_init(struct dispc_device *dispc) case DISPC_K2G: dispc_k2g_plane_init(dispc); break; + case DISPC_AM625: case DISPC_AM65X: case DISPC_J721E: dispc_k3_plane_init(dispc); @@ -2305,6 +2358,7 @@ static void dispc_vp_write_gamma_table(struct dispc_device *dispc, case DISPC_K2G: dispc_k2g_vp_write_gamma_table(dispc, hw_videoport); break; + case DISPC_AM625: case DISPC_AM65X: dispc_am65x_vp_write_gamma_table(dispc, hw_videoport); break; @@ -2579,7 +2633,8 @@ int dispc_runtime_resume(struct dispc_device *dispc) REG_GET(dispc, DSS_SYSSTATUS, 2, 2), REG_GET(dispc, DSS_SYSSTATUS, 3, 3)); - if (dispc->feat->subrev == DISPC_AM65X) + if (dispc->feat->subrev == DISPC_AM625 || + dispc->feat->subrev == DISPC_AM65X) dev_dbg(dispc->dev, "OLDI RESETDONE %d,%d,%d\n", REG_GET(dispc, DSS_SYSSTATUS, 5, 5), REG_GET(dispc, DSS_SYSSTATUS, 6, 6), diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h index 946ed769caaf..33ac5ad7a423 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.h +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -59,6 +59,7 @@ enum dispc_vp_bus_type { enum dispc_dss_subrevision { DISPC_K2G, + DISPC_AM625, DISPC_AM65X, DISPC_J721E, }; @@ -86,6 +87,7 @@ struct dispc_features { }; extern const struct dispc_features dispc_k2g_feats; +extern const struct dispc_features dispc_am625_feats; extern const struct dispc_features dispc_am65x_feats; extern const struct dispc_features dispc_j721e_feats; diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index 3f5f27fb6ebc..b88a0b123005 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -197,7 +197,7 @@ err_runtime_suspend: return ret; } -static int tidss_remove(struct platform_device *pdev) +static void tidss_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct tidss_device *tidss = platform_get_drvdata(pdev); @@ -221,8 +221,6 @@ static int tidss_remove(struct platform_device *pdev) dispc_remove(tidss); dev_dbg(dev, "%s done\n", __func__); - - return 0; } static void tidss_shutdown(struct platform_device *pdev) @@ -232,6 +230,7 @@ static void tidss_shutdown(struct platform_device *pdev) static const struct of_device_id tidss_of_table[] = { { .compatible = "ti,k2g-dss", .data = &dispc_k2g_feats, }, + { .compatible = "ti,am625-dss", .data = &dispc_am625_feats, }, { .compatible = "ti,am65x-dss", .data = &dispc_am65x_feats, }, { .compatible = "ti,j721e-dss", .data = &dispc_j721e_feats, }, { } @@ -241,7 +240,7 @@ MODULE_DEVICE_TABLE(of, tidss_of_table); static struct platform_driver tidss_platform_driver = { .probe = tidss_probe, - .remove = tidss_remove, + .remove_new = tidss_remove, .shutdown = tidss_shutdown, .driver = { .name = "tidss", diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c index 0d4865e9c03d..17a86bed8054 100644 --- a/drivers/gpu/drm/tidss/tidss_encoder.c +++ b/drivers/gpu/drm/tidss/tidss_encoder.c @@ -6,91 +6,125 @@ #include <linux/export.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_panel.h> #include <drm/drm_of.h> +#include <drm/drm_simple_kms_helper.h> #include "tidss_crtc.h" #include "tidss_drv.h" #include "tidss_encoder.h" -static int tidss_encoder_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +struct tidss_encoder { + struct drm_bridge bridge; + struct drm_encoder encoder; + struct drm_connector *connector; + struct drm_bridge *next_bridge; + struct tidss_device *tidss; +}; + +static inline struct tidss_encoder +*bridge_to_tidss_encoder(struct drm_bridge *b) +{ + return container_of(b, struct tidss_encoder, bridge); +} + +static int tidss_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct tidss_encoder *t_enc = bridge_to_tidss_encoder(bridge); + + return drm_bridge_attach(bridge->encoder, t_enc->next_bridge, + bridge, flags); +} + +static int tidss_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { - struct drm_device *ddev = encoder->dev; + struct tidss_encoder *t_enc = bridge_to_tidss_encoder(bridge); + struct tidss_device *tidss = t_enc->tidss; struct tidss_crtc_state *tcrtc_state = to_tidss_crtc_state(crtc_state); struct drm_display_info *di = &conn_state->connector->display_info; - struct drm_bridge *bridge; - bool bus_flags_set = false; - - dev_dbg(ddev->dev, "%s\n", __func__); - - /* - * Take the bus_flags from the first bridge that defines - * bridge timings, or from the connector's display_info if no - * bridge defines the timings. - */ - drm_for_each_bridge_in_chain(encoder, bridge) { - if (!bridge->timings) - continue; - - tcrtc_state->bus_flags = bridge->timings->input_bus_flags; - bus_flags_set = true; - break; - } + struct drm_bridge_state *next_bridge_state = NULL; + + if (t_enc->next_bridge) + next_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, + t_enc->next_bridge); - if (!di->bus_formats || di->num_bus_formats == 0) { - dev_err(ddev->dev, "%s: No bus_formats in connected display\n", + if (next_bridge_state) { + tcrtc_state->bus_flags = next_bridge_state->input_bus_cfg.flags; + tcrtc_state->bus_format = next_bridge_state->input_bus_cfg.format; + } else if (di->num_bus_formats) { + tcrtc_state->bus_format = di->bus_formats[0]; + tcrtc_state->bus_flags = di->bus_flags; + } else { + dev_err(tidss->dev, "%s: No bus_formats in connected display\n", __func__); return -EINVAL; } - // XXX any cleaner way to set bus format and flags? - tcrtc_state->bus_format = di->bus_formats[0]; - if (!bus_flags_set) - tcrtc_state->bus_flags = di->bus_flags; - return 0; } -static void tidss_encoder_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); - kfree(encoder); -} - -static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .atomic_check = tidss_encoder_atomic_check, -}; - -static const struct drm_encoder_funcs encoder_funcs = { - .destroy = tidss_encoder_destroy, +static const struct drm_bridge_funcs tidss_bridge_funcs = { + .attach = tidss_bridge_attach, + .atomic_check = tidss_bridge_atomic_check, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, }; -struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss, - u32 encoder_type, u32 possible_crtcs) +int tidss_encoder_create(struct tidss_device *tidss, + struct drm_bridge *next_bridge, + u32 encoder_type, u32 possible_crtcs) { + struct tidss_encoder *t_enc; struct drm_encoder *enc; + struct drm_connector *connector; int ret; - enc = kzalloc(sizeof(*enc), GFP_KERNEL); - if (!enc) - return ERR_PTR(-ENOMEM); + t_enc = drmm_simple_encoder_alloc(&tidss->ddev, struct tidss_encoder, + encoder, encoder_type); + if (IS_ERR(t_enc)) + return PTR_ERR(t_enc); + + t_enc->tidss = tidss; + t_enc->next_bridge = next_bridge; + t_enc->bridge.funcs = &tidss_bridge_funcs; + enc = &t_enc->encoder; enc->possible_crtcs = possible_crtcs; - ret = drm_encoder_init(&tidss->ddev, enc, &encoder_funcs, - encoder_type, NULL); - if (ret < 0) { - kfree(enc); - return ERR_PTR(ret); + /* Attaching first bridge to the encoder */ + ret = drm_bridge_attach(enc, &t_enc->bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) { + dev_err(tidss->dev, "bridge attach failed: %d\n", ret); + return ret; + } + + /* Initializing the connector at the end of bridge-chain */ + connector = drm_bridge_connector_init(&tidss->ddev, enc); + if (IS_ERR(connector)) { + dev_err(tidss->dev, "bridge_connector create failed\n"); + return PTR_ERR(connector); + } + + ret = drm_connector_attach_encoder(connector, enc); + if (ret) { + dev_err(tidss->dev, "attaching encoder to connector failed\n"); + return ret; } - drm_encoder_helper_add(enc, &encoder_helper_funcs); + t_enc->connector = connector; dev_dbg(tidss->dev, "Encoder create done\n"); - return enc; + return ret; } diff --git a/drivers/gpu/drm/tidss/tidss_encoder.h b/drivers/gpu/drm/tidss/tidss_encoder.h index ace877c0e0fd..3e561d6b1e83 100644 --- a/drivers/gpu/drm/tidss/tidss_encoder.h +++ b/drivers/gpu/drm/tidss/tidss_encoder.h @@ -11,7 +11,8 @@ struct tidss_device; -struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss, - u32 encoder_type, u32 possible_crtcs); +int tidss_encoder_create(struct tidss_device *tidss, + struct drm_bridge *next_bridge, + u32 encoder_type, u32 possible_crtcs); #endif diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index ad2fa3c3d4a7..c979ad1af236 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -193,7 +193,6 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) for (i = 0; i < num_pipes; ++i) { struct tidss_plane *tplane; struct tidss_crtc *tcrtc; - struct drm_encoder *enc; u32 hw_plane_id = feat->vid_order[tidss->num_planes]; int ret; @@ -216,16 +215,13 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) tidss->crtcs[tidss->num_crtcs++] = &tcrtc->crtc; - enc = tidss_encoder_create(tidss, pipes[i].enc_type, + ret = tidss_encoder_create(tidss, pipes[i].bridge, + pipes[i].enc_type, 1 << tcrtc->crtc.index); - if (IS_ERR(enc)) { + if (ret) { dev_err(tidss->dev, "encoder create failed\n"); - return PTR_ERR(enc); - } - - ret = drm_bridge_attach(enc, pipes[i].bridge, NULL, 0); - if (ret) return ret; + } } /* create overlay planes of the leftover planes */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 2729e16bc053..9aefd010acde 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -374,7 +374,7 @@ fail_backlight: return ret; } -static int panel_remove(struct platform_device *pdev) +static void panel_remove(struct platform_device *pdev) { struct tilcdc_module *mod = dev_get_platdata(&pdev->dev); struct panel_module *panel_mod = to_panel_module(mod); @@ -387,8 +387,6 @@ static int panel_remove(struct platform_device *pdev) tilcdc_module_cleanup(mod); kfree(panel_mod->info); - - return 0; } static const struct of_device_id panel_of_match[] = { @@ -398,7 +396,7 @@ static const struct of_device_id panel_of_match[] = { static struct platform_driver panel_driver = { .probe = panel_probe, - .remove = panel_remove, + .remove_new = panel_remove, .driver = { .name = "tilcdc-panel", .of_match_table = panel_of_match, diff --git a/drivers/gpu/drm/tiny/ofdrm.c b/drivers/gpu/drm/tiny/ofdrm.c index 76cd7f515bab..2d999a0facde 100644 --- a/drivers/gpu/drm/tiny/ofdrm.c +++ b/drivers/gpu/drm/tiny/ofdrm.c @@ -1369,13 +1369,11 @@ static int ofdrm_probe(struct platform_device *pdev) return 0; } -static int ofdrm_remove(struct platform_device *pdev) +static void ofdrm_remove(struct platform_device *pdev) { struct drm_device *dev = platform_get_drvdata(pdev); drm_dev_unplug(dev); - - return 0; } static const struct of_device_id ofdrm_of_match_display[] = { @@ -1390,7 +1388,7 @@ static struct platform_driver ofdrm_platform_driver = { .of_match_table = ofdrm_of_match_display, }, .probe = ofdrm_probe, - .remove = ofdrm_remove, + .remove_new = ofdrm_remove, }; module_platform_driver(ofdrm_platform_driver); diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index 25e11ef11c4c..ff86ba1ae1b8 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -888,14 +888,12 @@ static int simpledrm_probe(struct platform_device *pdev) return 0; } -static int simpledrm_remove(struct platform_device *pdev) +static void simpledrm_remove(struct platform_device *pdev) { struct simpledrm_device *sdev = platform_get_drvdata(pdev); struct drm_device *dev = &sdev->dev; drm_dev_unplug(dev); - - return 0; } static const struct of_device_id simpledrm_of_match_table[] = { @@ -910,7 +908,7 @@ static struct platform_driver simpledrm_platform_driver = { .of_match_table = simpledrm_of_match_table, }, .probe = simpledrm_probe, - .remove = simpledrm_remove, + .remove_new = simpledrm_remove, }; module_platform_driver(simpledrm_platform_driver); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 7139a522b2f3..aa8ab1a418af 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -345,6 +345,7 @@ static void ttm_bo_release(struct kref *kref) if (!dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP) || + (want_init_on_free() && (bo->ttm != NULL)) || !dma_resv_trylock(bo->base.resv)) { /* The BO is not idle, resurrect it for delayed destroy */ ttm_bo_flush_all_fences(bo); @@ -1160,7 +1161,6 @@ int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, * Move to system cached */ if (bo->resource->mem_type != TTM_PL_SYSTEM) { - struct ttm_operation_ctx ctx = { false, false }; struct ttm_resource *evict_mem; struct ttm_place hop; @@ -1170,7 +1170,7 @@ int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, if (unlikely(ret)) goto out; - ret = ttm_bo_handle_move_mem(bo, evict_mem, true, &ctx, &hop); + ret = ttm_bo_handle_move_mem(bo, evict_mem, true, ctx, &hop); if (unlikely(ret != 0)) { WARN(ret == -EMULTIHOP, "Unexpected multihop in swaput - likely driver bug.\n"); ttm_resource_free(bo, &evict_mem); diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c index 40b1168ad671..984aa8f0a542 100644 --- a/drivers/gpu/drm/tve200/tve200_drv.c +++ b/drivers/gpu/drm/tve200/tve200_drv.c @@ -236,7 +236,7 @@ dev_unref: return ret; } -static int tve200_remove(struct platform_device *pdev) +static void tve200_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); struct tve200_drm_dev_private *priv = drm->dev_private; @@ -247,8 +247,6 @@ static int tve200_remove(struct platform_device *pdev) drm_mode_config_cleanup(drm); clk_disable_unprepare(priv->pclk); drm_dev_put(drm); - - return 0; } static const struct of_device_id tve200_of_match[] = { @@ -264,7 +262,7 @@ static struct platform_driver tve200_driver = { .of_match_table = of_match_ptr(tve200_of_match), }, .probe = tve200_probe, - .remove = tve200_remove, + .remove_new = tve200_remove, }; drm_module_platform_driver(tve200_driver); diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index 478f1f0f60de..ffbbe9d527d3 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -171,10 +171,7 @@ static const struct drm_driver v3d_drm_driver = { #endif .gem_create_object = v3d_create_object, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = v3d_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .ioctls = v3d_drm_ioctls, .num_ioctls = ARRAY_SIZE(v3d_drm_ioctls), @@ -295,7 +292,7 @@ dma_free: return ret; } -static int v3d_platform_drm_remove(struct platform_device *pdev) +static void v3d_platform_drm_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); struct v3d_dev *v3d = to_v3d_dev(drm); @@ -306,13 +303,11 @@ static int v3d_platform_drm_remove(struct platform_device *pdev) dma_free_wc(v3d->drm.dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr); - - return 0; } static struct platform_driver v3d_platform_driver = { .probe = v3d_platform_drm_probe, - .remove = v3d_platform_drm_remove, + .remove_new = v3d_platform_drm_remove, .driver = { .name = "v3d", .of_match_table = v3d_of_match, diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index bef9d45ef1df..7610e841ef3c 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -1450,15 +1450,14 @@ static int vc4_crtc_dev_probe(struct platform_device *pdev) return component_add(&pdev->dev, &vc4_crtc_ops); } -static int vc4_crtc_dev_remove(struct platform_device *pdev) +static void vc4_crtc_dev_remove(struct platform_device *pdev) { component_del(&pdev->dev, &vc4_crtc_ops); - return 0; } struct platform_driver vc4_crtc_driver = { .probe = vc4_crtc_dev_probe, - .remove = vc4_crtc_dev_remove, + .remove_new = vc4_crtc_dev_remove, .driver = { .name = "vc4_crtc", .of_match_table = vc4_crtc_dt_match, diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index e68c07d86040..366c28ae2396 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -388,15 +388,14 @@ static int vc4_dpi_dev_probe(struct platform_device *pdev) return component_add(&pdev->dev, &vc4_dpi_ops); } -static int vc4_dpi_dev_remove(struct platform_device *pdev) +static void vc4_dpi_dev_remove(struct platform_device *pdev) { component_del(&pdev->dev, &vc4_dpi_ops); - return 0; } struct platform_driver vc4_dpi_driver = { .probe = vc4_dpi_dev_probe, - .remove = vc4_dpi_dev_remove, + .remove_new = vc4_dpi_dev_remove, .driver = { .name = "vc4_dpi", .of_match_table = vc4_dpi_dt_match, diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 823395c23cc3..c520cc93d332 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -439,11 +439,9 @@ static int vc4_platform_drm_probe(struct platform_device *pdev) return component_master_add_with_match(dev, &vc4_drm_ops, match); } -static int vc4_platform_drm_remove(struct platform_device *pdev) +static void vc4_platform_drm_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &vc4_drm_ops); - - return 0; } static const struct of_device_id vc4_of_match[] = { @@ -456,7 +454,7 @@ MODULE_DEVICE_TABLE(of, vc4_of_match); static struct platform_driver vc4_platform_driver = { .probe = vc4_platform_drm_probe, - .remove = vc4_platform_drm_remove, + .remove_new = vc4_platform_drm_remove, .driver = { .name = "vc4-drm", .of_match_table = vc4_of_match, diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 9e0c355b236f..5011fe0fef87 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1825,20 +1825,18 @@ static int vc4_dsi_dev_probe(struct platform_device *pdev) return 0; } -static int vc4_dsi_dev_remove(struct platform_device *pdev) +static void vc4_dsi_dev_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct vc4_dsi *dsi = dev_get_drvdata(dev); mipi_dsi_host_unregister(&dsi->dsi_host); vc4_dsi_put(dsi); - - return 0; } struct platform_driver vc4_dsi_driver = { .probe = vc4_dsi_dev_probe, - .remove = vc4_dsi_dev_remove, + .remove_new = vc4_dsi_dev_remove, .driver = { .name = "vc4_dsi", .of_match_table = vc4_dsi_dt_match, diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 5261526d286f..56a950e6d777 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -3770,10 +3770,9 @@ static int vc4_hdmi_dev_probe(struct platform_device *pdev) return component_add(&pdev->dev, &vc4_hdmi_ops); } -static int vc4_hdmi_dev_remove(struct platform_device *pdev) +static void vc4_hdmi_dev_remove(struct platform_device *pdev) { component_del(&pdev->dev, &vc4_hdmi_ops); - return 0; } static const struct vc4_hdmi_variant bcm2835_variant = { @@ -3869,7 +3868,7 @@ static const struct dev_pm_ops vc4_hdmi_pm_ops = { struct platform_driver vc4_hdmi_driver = { .probe = vc4_hdmi_dev_probe, - .remove = vc4_hdmi_dev_remove, + .remove_new = vc4_hdmi_dev_remove, .driver = { .name = "vc4_hdmi", .of_match_table = vc4_hdmi_dt_match, diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 4da66ef96783..04af672caacb 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -1061,10 +1061,9 @@ static int vc4_hvs_dev_probe(struct platform_device *pdev) return component_add(&pdev->dev, &vc4_hvs_ops); } -static int vc4_hvs_dev_remove(struct platform_device *pdev) +static void vc4_hvs_dev_remove(struct platform_device *pdev) { component_del(&pdev->dev, &vc4_hvs_ops); - return 0; } static const struct of_device_id vc4_hvs_dt_match[] = { @@ -1075,7 +1074,7 @@ static const struct of_device_id vc4_hvs_dt_match[] = { struct platform_driver vc4_hvs_driver = { .probe = vc4_hvs_dev_probe, - .remove = vc4_hvs_dev_remove, + .remove_new = vc4_hvs_dev_remove, .driver = { .name = "vc4_hvs", .of_match_table = vc4_hvs_dt_match, diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index c5abdec03103..6cdd97f364a8 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -573,10 +573,9 @@ static int vc4_txp_probe(struct platform_device *pdev) return component_add(&pdev->dev, &vc4_txp_ops); } -static int vc4_txp_remove(struct platform_device *pdev) +static void vc4_txp_remove(struct platform_device *pdev) { component_del(&pdev->dev, &vc4_txp_ops); - return 0; } static const struct of_device_id vc4_txp_dt_match[] = { @@ -586,7 +585,7 @@ static const struct of_device_id vc4_txp_dt_match[] = { struct platform_driver vc4_txp_driver = { .probe = vc4_txp_probe, - .remove = vc4_txp_remove, + .remove_new = vc4_txp_remove, .driver = { .name = "vc4_txp", .of_match_table = vc4_txp_dt_match, diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index 29a664c8bf44..04ac7805e6d5 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -532,10 +532,9 @@ static int vc4_v3d_dev_probe(struct platform_device *pdev) return component_add(&pdev->dev, &vc4_v3d_ops); } -static int vc4_v3d_dev_remove(struct platform_device *pdev) +static void vc4_v3d_dev_remove(struct platform_device *pdev) { component_del(&pdev->dev, &vc4_v3d_ops); - return 0; } const struct of_device_id vc4_v3d_dt_match[] = { @@ -547,7 +546,7 @@ const struct of_device_id vc4_v3d_dt_match[] = { struct platform_driver vc4_v3d_driver = { .probe = vc4_v3d_dev_probe, - .remove = vc4_v3d_dev_remove, + .remove_new = vc4_v3d_dev_remove, .driver = { .name = "vc4_v3d", .of_match_table = vc4_v3d_dt_match, diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index d6e6a1a22eba..725531d030a1 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -812,15 +812,14 @@ static int vc4_vec_dev_probe(struct platform_device *pdev) return component_add(&pdev->dev, &vc4_vec_ops); } -static int vc4_vec_dev_remove(struct platform_device *pdev) +static void vc4_vec_dev_remove(struct platform_device *pdev) { component_del(&pdev->dev, &vc4_vec_ops); - return 0; } struct platform_driver vc4_vec_driver = { .probe = vc4_vec_dev_probe, - .remove = vc4_vec_dev_remove, + .remove_new = vc4_vec_dev_remove, .driver = { .name = "vc4_vec", .of_match_table = vc4_vec_dt_match, diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index add075681e18..a7ec5a3770da 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -186,9 +186,6 @@ static const struct drm_driver driver = { #if defined(CONFIG_DEBUG_FS) .debugfs_init = virtio_gpu_debugfs_init, #endif - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_mmap = drm_gem_prime_mmap, .gem_prime_import = virtgpu_gem_prime_import, .gem_prime_import_sg_table = virtgpu_gem_prime_import_sg_table, diff --git a/drivers/gpu/drm/virtio/virtgpu_submit.c b/drivers/gpu/drm/virtio/virtgpu_submit.c index cf3c04b16a7a..1d010c66910d 100644 --- a/drivers/gpu/drm/virtio/virtgpu_submit.c +++ b/drivers/gpu/drm/virtio/virtgpu_submit.c @@ -64,13 +64,9 @@ static int virtio_gpu_fence_event_create(struct drm_device *dev, struct virtio_gpu_fence *fence, u32 ring_idx) { - struct virtio_gpu_fpriv *vfpriv = file->driver_priv; struct virtio_gpu_fence_event *e = NULL; int ret; - if (!(vfpriv->ring_idx_mask & BIT_ULL(ring_idx))) - return 0; - e = kzalloc(sizeof(*e), GFP_KERNEL); if (!e) return -ENOMEM; @@ -164,18 +160,30 @@ static int virtio_gpu_init_submit(struct virtio_gpu_submit *submit, struct virtio_gpu_fpriv *vfpriv = file->driver_priv; struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_fence *out_fence; + bool drm_fence_event; int err; memset(submit, 0, sizeof(*submit)); - out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx); - if (!out_fence) - return -ENOMEM; - - err = virtio_gpu_fence_event_create(dev, file, out_fence, ring_idx); - if (err) { - dma_fence_put(&out_fence->f); - return err; + if ((exbuf->flags & VIRTGPU_EXECBUF_RING_IDX) && + (vfpriv->ring_idx_mask & BIT_ULL(ring_idx))) + drm_fence_event = true; + else + drm_fence_event = false; + + if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) || + exbuf->num_bo_handles || + drm_fence_event) + out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx); + else + out_fence = NULL; + + if (drm_fence_event) { + err = virtio_gpu_fence_event_create(dev, file, out_fence, ring_idx); + if (err) { + dma_fence_put(&out_fence->f); + return err; + } } submit->out_fence = out_fence; diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 906d3df40cdb..d170a8e89b95 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -23,7 +23,7 @@ static u16 pre_mul_blend_channel(u16 src, u16 dst, u16 alpha) /** * pre_mul_alpha_blend - alpha blending equation - * @src_frame_info: source framebuffer's metadata + * @frame_info: Source framebuffer's metadata * @stage_buffer: The line with the pixels from src_plane * @output_buffer: A line buffer that receives all the blends output * @@ -90,11 +90,13 @@ static void fill_background(const struct pixel_argb_u16 *background_color, } /** - * @wb_frame_info: The writeback frame buffer metadata + * blend - blend the pixels from all planes and compute crc + * @wb: The writeback frame buffer metadata * @crtc_state: The crtc state * @crc32: The crc output of the final frame * @output_buffer: A buffer of a row that will receive the result of the blend(s) * @stage_buffer: The line with the pixels from plane being blend to the output + * @row_size: The size, in bytes, of a single row * * This function blends the pixels (Using the `pre_mul_alpha_blend`) * from all planes, calculates the crc32 of the output from the former step, @@ -131,7 +133,7 @@ static void blend(struct vkms_writeback_job *wb, *crc32 = crc32_le(*crc32, (void *)output_buffer->pixels, row_size); if (wb) - wb->wb_write(&wb->wb_frame_info, output_buffer, y_pos); + vkms_writeback_row(wb, output_buffer, y_pos); } } @@ -145,7 +147,7 @@ static int check_format_funcs(struct vkms_crtc_state *crtc_state, if (!planes[i]->pixel_read) return -1; - if (active_wb && !active_wb->wb_write) + if (active_wb && !active_wb->pixel_write) return -1; return 0; diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 5f1a0a44a78c..d25e0aae91f2 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -46,8 +46,7 @@ struct line_buffer { struct vkms_writeback_job { struct iosys_map data[DRM_FORMAT_MAX_PLANES]; struct vkms_frame_info wb_frame_info; - void (*wb_write)(struct vkms_frame_info *frame_info, - const struct line_buffer *buffer, int y); + void (*pixel_write)(u8 *dst_pixels, struct pixel_argb_u16 *in_pixel); }; /** @@ -157,6 +156,7 @@ int vkms_verify_crc_source(struct drm_crtc *crtc, const char *source_name, void vkms_composer_worker(struct work_struct *work); void vkms_set_composer(struct vkms_output *out, bool enabled); void vkms_compose_row(struct line_buffer *stage_buffer, struct vkms_plane_state *plane, int y); +void vkms_writeback_row(struct vkms_writeback_job *wb, const struct line_buffer *src_buffer, int y); /* Writeback */ int vkms_enable_writeback_connector(struct vkms_device *vkmsdev); diff --git a/drivers/gpu/drm/vkms/vkms_formats.c b/drivers/gpu/drm/vkms/vkms_formats.c index 5945da0beba6..36046b12f296 100644 --- a/drivers/gpu/drm/vkms/vkms_formats.c +++ b/drivers/gpu/drm/vkms/vkms_formats.c @@ -111,6 +111,19 @@ static void RGB565_to_argb_u16(u8 *src_pixels, struct pixel_argb_u16 *out_pixel) out_pixel->b = drm_fixp2int_round(drm_fixp_mul(fp_b, fp_rb_ratio)); } +/** + * vkms_compose_row - compose a single row of a plane + * @stage_buffer: output line with the composed pixels + * @plane: state of the plane that is being composed + * @y: y coordinate of the row + * + * This function composes a single row of a plane. It gets the source pixels + * through the y coordinate (see get_packed_src_addr()) and goes linearly + * through the source pixel, reading the pixels and converting it to + * ARGB16161616 (see the pixel_read() callback). For rotate-90 and rotate-270, + * the source pixels are not traversed linearly. The source pixels are queried + * on each iteration in order to traverse the pixels vertically. + */ void vkms_compose_row(struct line_buffer *stage_buffer, struct vkms_plane_state *plane, int y) { struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; @@ -137,107 +150,81 @@ void vkms_compose_row(struct line_buffer *stage_buffer, struct vkms_plane_state * They are used in the `compose_active_planes` to convert and store a line * from the src_buffer to the writeback buffer. */ -static void argb_u16_to_ARGB8888(struct vkms_frame_info *frame_info, - const struct line_buffer *src_buffer, int y) +static void argb_u16_to_ARGB8888(u8 *dst_pixels, struct pixel_argb_u16 *in_pixel) { - int x_dst = frame_info->dst.x1; - u8 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y); - struct pixel_argb_u16 *in_pixels = src_buffer->pixels; - int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), - src_buffer->n_pixels); - - for (size_t x = 0; x < x_limit; x++, dst_pixels += 4) { - /* - * This sequence below is important because the format's byte order is - * in little-endian. In the case of the ARGB8888 the memory is - * organized this way: - * - * | Addr | = blue channel - * | Addr + 1 | = green channel - * | Addr + 2 | = Red channel - * | Addr + 3 | = Alpha channel - */ - dst_pixels[3] = DIV_ROUND_CLOSEST(in_pixels[x].a, 257); - dst_pixels[2] = DIV_ROUND_CLOSEST(in_pixels[x].r, 257); - dst_pixels[1] = DIV_ROUND_CLOSEST(in_pixels[x].g, 257); - dst_pixels[0] = DIV_ROUND_CLOSEST(in_pixels[x].b, 257); - } + /* + * This sequence below is important because the format's byte order is + * in little-endian. In the case of the ARGB8888 the memory is + * organized this way: + * + * | Addr | = blue channel + * | Addr + 1 | = green channel + * | Addr + 2 | = Red channel + * | Addr + 3 | = Alpha channel + */ + dst_pixels[3] = DIV_ROUND_CLOSEST(in_pixel->a, 257); + dst_pixels[2] = DIV_ROUND_CLOSEST(in_pixel->r, 257); + dst_pixels[1] = DIV_ROUND_CLOSEST(in_pixel->g, 257); + dst_pixels[0] = DIV_ROUND_CLOSEST(in_pixel->b, 257); } -static void argb_u16_to_XRGB8888(struct vkms_frame_info *frame_info, - const struct line_buffer *src_buffer, int y) +static void argb_u16_to_XRGB8888(u8 *dst_pixels, struct pixel_argb_u16 *in_pixel) { - int x_dst = frame_info->dst.x1; - u8 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y); - struct pixel_argb_u16 *in_pixels = src_buffer->pixels; - int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), - src_buffer->n_pixels); - - for (size_t x = 0; x < x_limit; x++, dst_pixels += 4) { - dst_pixels[3] = 0xff; - dst_pixels[2] = DIV_ROUND_CLOSEST(in_pixels[x].r, 257); - dst_pixels[1] = DIV_ROUND_CLOSEST(in_pixels[x].g, 257); - dst_pixels[0] = DIV_ROUND_CLOSEST(in_pixels[x].b, 257); - } + dst_pixels[3] = 0xff; + dst_pixels[2] = DIV_ROUND_CLOSEST(in_pixel->r, 257); + dst_pixels[1] = DIV_ROUND_CLOSEST(in_pixel->g, 257); + dst_pixels[0] = DIV_ROUND_CLOSEST(in_pixel->b, 257); } -static void argb_u16_to_ARGB16161616(struct vkms_frame_info *frame_info, - const struct line_buffer *src_buffer, int y) +static void argb_u16_to_ARGB16161616(u8 *dst_pixels, struct pixel_argb_u16 *in_pixel) { - int x_dst = frame_info->dst.x1; - u16 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y); - struct pixel_argb_u16 *in_pixels = src_buffer->pixels; - int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), - src_buffer->n_pixels); - - for (size_t x = 0; x < x_limit; x++, dst_pixels += 4) { - dst_pixels[3] = cpu_to_le16(in_pixels[x].a); - dst_pixels[2] = cpu_to_le16(in_pixels[x].r); - dst_pixels[1] = cpu_to_le16(in_pixels[x].g); - dst_pixels[0] = cpu_to_le16(in_pixels[x].b); - } + u16 *pixels = (u16 *)dst_pixels; + + pixels[3] = cpu_to_le16(in_pixel->a); + pixels[2] = cpu_to_le16(in_pixel->r); + pixels[1] = cpu_to_le16(in_pixel->g); + pixels[0] = cpu_to_le16(in_pixel->b); } -static void argb_u16_to_XRGB16161616(struct vkms_frame_info *frame_info, - const struct line_buffer *src_buffer, int y) +static void argb_u16_to_XRGB16161616(u8 *dst_pixels, struct pixel_argb_u16 *in_pixel) { - int x_dst = frame_info->dst.x1; - u16 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y); - struct pixel_argb_u16 *in_pixels = src_buffer->pixels; - int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), - src_buffer->n_pixels); - - for (size_t x = 0; x < x_limit; x++, dst_pixels += 4) { - dst_pixels[3] = 0xffff; - dst_pixels[2] = cpu_to_le16(in_pixels[x].r); - dst_pixels[1] = cpu_to_le16(in_pixels[x].g); - dst_pixels[0] = cpu_to_le16(in_pixels[x].b); - } + u16 *pixels = (u16 *)dst_pixels; + + pixels[3] = 0xffff; + pixels[2] = cpu_to_le16(in_pixel->r); + pixels[1] = cpu_to_le16(in_pixel->g); + pixels[0] = cpu_to_le16(in_pixel->b); } -static void argb_u16_to_RGB565(struct vkms_frame_info *frame_info, - const struct line_buffer *src_buffer, int y) +static void argb_u16_to_RGB565(u8 *dst_pixels, struct pixel_argb_u16 *in_pixel) { - int x_dst = frame_info->dst.x1; - u16 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y); - struct pixel_argb_u16 *in_pixels = src_buffer->pixels; - int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), - src_buffer->n_pixels); + u16 *pixels = (u16 *)dst_pixels; s64 fp_rb_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(31)); s64 fp_g_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(63)); - for (size_t x = 0; x < x_limit; x++, dst_pixels++) { - s64 fp_r = drm_int2fixp(in_pixels[x].r); - s64 fp_g = drm_int2fixp(in_pixels[x].g); - s64 fp_b = drm_int2fixp(in_pixels[x].b); + s64 fp_r = drm_int2fixp(in_pixel->r); + s64 fp_g = drm_int2fixp(in_pixel->g); + s64 fp_b = drm_int2fixp(in_pixel->b); - u16 r = drm_fixp2int_round(drm_fixp_div(fp_r, fp_rb_ratio)); - u16 g = drm_fixp2int_round(drm_fixp_div(fp_g, fp_g_ratio)); - u16 b = drm_fixp2int_round(drm_fixp_div(fp_b, fp_rb_ratio)); + u16 r = drm_fixp2int(drm_fixp_div(fp_r, fp_rb_ratio)); + u16 g = drm_fixp2int(drm_fixp_div(fp_g, fp_g_ratio)); + u16 b = drm_fixp2int(drm_fixp_div(fp_b, fp_rb_ratio)); - *dst_pixels = cpu_to_le16(r << 11 | g << 5 | b); - } + *pixels = cpu_to_le16(r << 11 | g << 5 | b); +} + +void vkms_writeback_row(struct vkms_writeback_job *wb, + const struct line_buffer *src_buffer, int y) +{ + struct vkms_frame_info *frame_info = &wb->wb_frame_info; + int x_dst = frame_info->dst.x1; + u8 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y); + struct pixel_argb_u16 *in_pixels = src_buffer->pixels; + int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), src_buffer->n_pixels); + + for (size_t x = 0; x < x_limit; x++, dst_pixels += frame_info->cpp) + wb->pixel_write(dst_pixels, &in_pixels[x]); } void *get_pixel_conversion_function(u32 format) @@ -258,7 +245,7 @@ void *get_pixel_conversion_function(u32 format) } } -void *get_line_to_frame_function(u32 format) +void *get_pixel_write_function(u32 format) { switch (format) { case DRM_FORMAT_ARGB8888: diff --git a/drivers/gpu/drm/vkms/vkms_formats.h b/drivers/gpu/drm/vkms/vkms_formats.h index c5b113495d0c..cf59c2ed8e9a 100644 --- a/drivers/gpu/drm/vkms/vkms_formats.h +++ b/drivers/gpu/drm/vkms/vkms_formats.h @@ -7,6 +7,6 @@ void *get_pixel_conversion_function(u32 format); -void *get_line_to_frame_function(u32 format); +void *get_pixel_write_function(u32 format); #endif /* _VKMS_FORMATS_H_ */ diff --git a/drivers/gpu/drm/vkms/vkms_writeback.c b/drivers/gpu/drm/vkms/vkms_writeback.c index 84a51cd281b9..d7e63aa14663 100644 --- a/drivers/gpu/drm/vkms/vkms_writeback.c +++ b/drivers/gpu/drm/vkms/vkms_writeback.c @@ -15,6 +15,7 @@ #include "vkms_formats.h" static const u32 vkms_wb_formats[] = { + DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XRGB16161616, DRM_FORMAT_ARGB16161616, @@ -142,13 +143,15 @@ static void vkms_wb_atomic_commit(struct drm_connector *conn, spin_lock_irq(&output->composer_lock); crtc_state->active_writeback = active_wb; + crtc_state->wb_pending = true; + spin_unlock_irq(&output->composer_lock); + wb_frame_info->offset = fb->offsets[0]; wb_frame_info->pitch = fb->pitches[0]; wb_frame_info->cpp = fb->format->cpp[0]; - crtc_state->wb_pending = true; - spin_unlock_irq(&output->composer_lock); + drm_writeback_queue_job(wb_conn, connector_state); - active_wb->wb_write = get_line_to_frame_function(wb_format); + active_wb->pixel_write = get_pixel_write_function(wb_format); drm_rect_init(&wb_frame_info->src, 0, 0, crtc_width, crtc_height); drm_rect_init(&wb_frame_info->dst, 0, 0, crtc_width, crtc_height); } diff --git a/drivers/gpu/drm/xen/xen_drm_front.c b/drivers/gpu/drm/xen/xen_drm_front.c index 90996c108146..7e9431c50c5a 100644 --- a/drivers/gpu/drm/xen/xen_drm_front.c +++ b/drivers/gpu/drm/xen/xen_drm_front.c @@ -474,10 +474,7 @@ DEFINE_DRM_GEM_FOPS(xen_drm_dev_fops); static const struct drm_driver xen_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .release = xen_drm_drv_release, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = xen_drm_front_gem_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .dumb_create = xen_drm_drv_dumb_create, .fops = &xen_drm_dev_fops, .name = "xendrm-du", diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index bab862484d42..9d64a8d98372 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -280,7 +280,7 @@ err_mem: return ret; } -static int zynqmp_dpsub_remove(struct platform_device *pdev) +static void zynqmp_dpsub_remove(struct platform_device *pdev) { struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); @@ -298,8 +298,6 @@ static int zynqmp_dpsub_remove(struct platform_device *pdev) if (!dpsub->drm) zynqmp_dpsub_release(dpsub); - - return 0; } static void zynqmp_dpsub_shutdown(struct platform_device *pdev) @@ -320,7 +318,7 @@ MODULE_DEVICE_TABLE(of, zynqmp_dpsub_of_match); static struct platform_driver zynqmp_dpsub_driver = { .probe = zynqmp_dpsub_probe, - .remove = zynqmp_dpsub_remove, + .remove_new = zynqmp_dpsub_remove, .shutdown = zynqmp_dpsub_shutdown, .driver = { .name = "zynqmp-dpsub", diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index 205d3cac425c..2fa455d4a048 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -11,7 +11,6 @@ */ #include <linux/dma-buf.h> -#include <linux/dma-resv.h> #include <linux/module.h> #include <linux/refcount.h> #include <linux/scatterlist.h> @@ -456,8 +455,6 @@ static int vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf, struct iosys_map *map) static int vb2_dc_dmabuf_ops_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) { - dma_resv_assert_held(dbuf->resv); - return vb2_dc_mmap(dbuf->priv, vma); } diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 183037fb1273..28f3fdfe23a2 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -10,7 +10,6 @@ * the Free Software Foundation. */ -#include <linux/dma-resv.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/refcount.h> @@ -498,8 +497,6 @@ static int vb2_dma_sg_dmabuf_ops_vmap(struct dma_buf *dbuf, static int vb2_dma_sg_dmabuf_ops_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) { - dma_resv_assert_held(dbuf->resv); - return vb2_dma_sg_mmap(dbuf->priv, vma); } diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c b/drivers/media/common/videobuf2/videobuf2-vmalloc.c index a6c6d2fcaaa4..7c635e292106 100644 --- a/drivers/media/common/videobuf2/videobuf2-vmalloc.c +++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c @@ -10,7 +10,6 @@ * the Free Software Foundation. */ -#include <linux/dma-resv.h> #include <linux/io.h> #include <linux/module.h> #include <linux/mm.h> @@ -319,8 +318,6 @@ static int vb2_vmalloc_dmabuf_ops_vmap(struct dma_buf *dbuf, static int vb2_vmalloc_dmabuf_ops_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) { - dma_resv_assert_held(dbuf->resv); - return vb2_vmalloc_mmap(dbuf->priv, vma); } diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig index 4d29e8c1014e..5dda3c65a38e 100644 --- a/drivers/staging/fbtft/Kconfig +++ b/drivers/staging/fbtft/Kconfig @@ -2,6 +2,7 @@ menuconfig FB_TFT tristate "Support for small TFT LCD display modules" depends on FB && SPI + depends on FB_DEVICE depends on GPIOLIB || COMPILE_TEST select FB_SYS_FILLRECT select FB_SYS_COPYAREA diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index 55e302a27847..c260f73cf570 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -14,7 +14,6 @@ #include <linux/mm_types.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> -#include <linux/screen_info.h> #include <linux/console.h> #include "sm750.h" diff --git a/drivers/staging/sm750fb/sm750_accel.c b/drivers/staging/sm750fb/sm750_accel.c index 24b9077a634a..44b9e3fe3a41 100644 --- a/drivers/staging/sm750fb/sm750_accel.c +++ b/drivers/staging/sm750fb/sm750_accel.c @@ -14,7 +14,6 @@ #include <linux/pagemap.h> #include <linux/console.h> #include <linux/platform_device.h> -#include <linux/screen_info.h> #include "sm750.h" #include "sm750_accel.h" diff --git a/drivers/staging/sm750fb/sm750_cursor.c b/drivers/staging/sm750fb/sm750_cursor.c index 43e6f52c2551..eea4d1bd36ce 100644 --- a/drivers/staging/sm750fb/sm750_cursor.c +++ b/drivers/staging/sm750fb/sm750_cursor.c @@ -14,7 +14,6 @@ #include <linux/pagemap.h> #include <linux/console.h> #include <linux/platform_device.h> -#include <linux/screen_info.h> #include "sm750.h" #include "sm750_cursor.h" diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c index 55cb00e8b0d1..71247eaf26ee 100644 --- a/drivers/staging/sm750fb/sm750_hw.c +++ b/drivers/staging/sm750fb/sm750_hw.c @@ -17,7 +17,6 @@ #include <asm/mtrr.h> #endif #include <linux/platform_device.h> -#include <linux/screen_info.h> #include <linux/sizes.h> #include "sm750.h" diff --git a/drivers/video/backlight/bd6107.c b/drivers/video/backlight/bd6107.c index 7df25faa07a5..c95a12bf0ce2 100644 --- a/drivers/video/backlight/bd6107.c +++ b/drivers/video/backlight/bd6107.c @@ -104,7 +104,7 @@ static int bd6107_backlight_check_fb(struct backlight_device *backlight, { struct bd6107 *bd = bl_get_data(backlight); - return bd->pdata->fbdev == NULL || bd->pdata->fbdev == info->dev; + return !bd->pdata->dev || bd->pdata->dev == info->device; } static const struct backlight_ops bd6107_backlight_ops = { diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c index 6f78d928f054..d3bea42407f1 100644 --- a/drivers/video/backlight/gpio_backlight.c +++ b/drivers/video/backlight/gpio_backlight.c @@ -17,7 +17,7 @@ #include <linux/slab.h> struct gpio_backlight { - struct device *fbdev; + struct device *dev; struct gpio_desc *gpiod; }; @@ -35,7 +35,7 @@ static int gpio_backlight_check_fb(struct backlight_device *bl, { struct gpio_backlight *gbl = bl_get_data(bl); - return gbl->fbdev == NULL || gbl->fbdev == info->dev; + return !gbl->dev || gbl->dev == info->device; } static const struct backlight_ops gpio_backlight_ops = { @@ -59,7 +59,7 @@ static int gpio_backlight_probe(struct platform_device *pdev) return -ENOMEM; if (pdata) - gbl->fbdev = pdata->fbdev; + gbl->dev = pdata->dev; def_value = device_property_read_bool(dev, "default-on"); diff --git a/drivers/video/backlight/lv5207lp.c b/drivers/video/backlight/lv5207lp.c index 56695ce67e48..1f1d06b4e119 100644 --- a/drivers/video/backlight/lv5207lp.c +++ b/drivers/video/backlight/lv5207lp.c @@ -67,7 +67,7 @@ static int lv5207lp_backlight_check_fb(struct backlight_device *backlight, { struct lv5207lp *lv = bl_get_data(backlight); - return lv->pdata->fbdev == NULL || lv->pdata->fbdev == info->dev; + return !lv->pdata->dev || lv->pdata->dev == info->device; } static const struct backlight_ops lv5207lp_backlight_ops = { diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 6df9bd09454a..c12c166be7d1 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -57,6 +57,16 @@ config FIRMWARE_EDID combination with certain motherboards and monitors are known to suffer from this problem. +config FB_DEVICE + bool "Provide legacy /dev/fb* device" + depends on FB + default y + help + Say Y here if you want the legacy /dev/fb* device file and + interfaces within sysfs anc procfs. It is only required if you + have userspace programs that depend on fbdev for graphics output. + This does not affect the framebuffer console. If unsure, say N. + config FB_DDC tristate depends on FB @@ -182,7 +192,7 @@ config FB_SYS_HELPERS_DEFERRED config FB_HECUBA tristate depends on FB - depends on FB_DEFERRED_IO + select FB_SYS_HELPERS_DEFERRED config FB_SVGALIB tristate @@ -673,11 +683,6 @@ config FB_EFI config FB_N411 tristate "N411 Apollo/Hecuba devkit support" depends on FB && X86 && MMU - select FB_SYS_FILLRECT - select FB_SYS_COPYAREA - select FB_SYS_IMAGEBLIT - select FB_SYS_FOPS - select FB_DEFERRED_IO select FB_HECUBA help This enables support for the Apollo display controller in its @@ -1545,6 +1550,7 @@ config FB_3DFX_I2C config FB_VOODOO1 tristate "3Dfx Voodoo Graphics (sst1) support" depends on FB && PCI + depends on FB_DEVICE select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -1862,6 +1868,7 @@ config FB_SH_MOBILE_LCDC tristate "SuperH Mobile LCDC framebuffer support" depends on FB && HAVE_CLK && HAS_IOMEM depends on SUPERH || ARCH_RENESAS || COMPILE_TEST + depends on FB_DEVICE select FB_SYS_FILLRECT select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT @@ -1930,6 +1937,7 @@ config FB_SMSCUFX config FB_UDL tristate "Displaylink USB Framebuffer support" depends on FB && USB + depends on FB_DEVICE select FB_MODE_HELPERS select FB_SYS_FILLRECT select FB_SYS_COPYAREA @@ -2057,11 +2065,7 @@ config FB_VIRTUAL config XEN_FBDEV_FRONTEND tristate "Xen virtual frame buffer support" depends on FB && XEN - select FB_SYS_FILLRECT - select FB_SYS_COPYAREA - select FB_SYS_IMAGEBLIT - select FB_SYS_FOPS - select FB_DEFERRED_IO + select FB_SYS_HELPERS_DEFERRED select XEN_XENBUS_FRONTEND default y help @@ -2072,11 +2076,7 @@ config XEN_FBDEV_FRONTEND config FB_METRONOME tristate "E-Ink Metronome/8track controller support" depends on FB - select FB_SYS_FILLRECT - select FB_SYS_COPYAREA - select FB_SYS_IMAGEBLIT - select FB_SYS_FOPS - select FB_DEFERRED_IO + select FB_SYS_HELPERS_DEFERRED help This driver implements support for the E-Ink Metronome controller. The pre-release name for this device was 8track @@ -2158,11 +2158,7 @@ config FB_MX3 config FB_BROADSHEET tristate "E-Ink Broadsheet/Epson S1D13521 controller support" depends on FB && (ARCH_PXA || COMPILE_TEST) - select FB_SYS_FILLRECT - select FB_SYS_COPYAREA - select FB_SYS_IMAGEBLIT - select FB_SYS_FOPS - select FB_DEFERRED_IO + select FB_SYS_HELPERS_DEFERRED help This driver implements support for the E-Ink Broadsheet controller. The release name for this device was Epson S1D13521 @@ -2203,12 +2199,8 @@ config FB_SSD1307 tristate "Solomon SSD1307 framebuffer support" depends on FB && I2C depends on GPIOLIB || COMPILE_TEST - select FB_SYS_FOPS - select FB_SYS_FILLRECT - select FB_SYS_COPYAREA - select FB_SYS_IMAGEBLIT - select FB_DEFERRED_IO select FB_BACKLIGHT + select FB_SYS_HELPERS_DEFERRED help This driver implements support for the Solomon SSD1307 OLED controller over I2C. diff --git a/drivers/video/fbdev/aty/aty128fb.c b/drivers/video/fbdev/aty/aty128fb.c index 36a9ac05a340..2d9320a52e51 100644 --- a/drivers/video/fbdev/aty/aty128fb.c +++ b/drivers/video/fbdev/aty/aty128fb.c @@ -1846,7 +1846,7 @@ static void aty128_bl_init(struct aty128fb_par *par) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = FB_BACKLIGHT_LEVELS - 1; - bd = backlight_device_register(name, info->dev, par, &aty128_bl_data, + bd = backlight_device_register(name, info->device, par, &aty128_bl_data, &props); if (IS_ERR(bd)) { info->bl_dev = NULL; @@ -2028,14 +2028,14 @@ static int aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent) par->asleep = 0; par->lock_blank = 0; + if (register_framebuffer(info) < 0) + return 0; + #ifdef CONFIG_FB_ATY128_BACKLIGHT if (backlight) aty128_bl_init(par); #endif - if (register_framebuffer(info) < 0) - return 0; - fb_info(info, "%s frame buffer device on %s\n", info->fix.id, video_card); @@ -2167,12 +2167,12 @@ static void aty128_remove(struct pci_dev *pdev) par = info->par; - unregister_framebuffer(info); - #ifdef CONFIG_FB_ATY128_BACKLIGHT aty128_bl_exit(info->bl_dev); #endif + unregister_framebuffer(info); + arch_phys_wc_del(par->wc_cookie); iounmap(par->regbase); iounmap(info->screen_base); diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index cba2b113b28b..e1602e3fbc66 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -2255,7 +2255,7 @@ static void aty_bl_init(struct atyfb_par *par) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = FB_BACKLIGHT_LEVELS - 1; - bd = backlight_device_register(name, info->dev, par, &aty_bl_data, + bd = backlight_device_register(name, info->device, par, &aty_bl_data, &props); if (IS_ERR(bd)) { info->bl_dev = NULL; @@ -2654,11 +2654,6 @@ static int aty_init(struct fb_info *info) USE_F32KHZ | TRISTATE_MEM_EN, par); } else #endif - if (M64_HAS(MOBIL_BUS) && backlight) { -#ifdef CONFIG_FB_ATY_BACKLIGHT - aty_bl_init(par); -#endif - } memset(&var, 0, sizeof(var)); #ifdef CONFIG_PPC @@ -2751,6 +2746,12 @@ static int aty_init(struct fb_info *info) goto aty_init_exit; } + if (M64_HAS(MOBIL_BUS) && backlight) { +#ifdef CONFIG_FB_ATY_BACKLIGHT + aty_bl_init(par); +#endif + } + fb_list = info; PRINTKI("fb%d: %s frame buffer device on %s\n", @@ -3716,12 +3717,13 @@ static void atyfb_remove(struct fb_info *info) aty_set_crtc(par, &par->saved_crtc); par->pll_ops->set_pll(info, &par->saved_pll); - unregister_framebuffer(info); - #ifdef CONFIG_FB_ATY_BACKLIGHT if (M64_HAS(MOBIL_BUS)) aty_bl_exit(info->bl_dev); #endif + + unregister_framebuffer(info); + arch_phys_wc_del(par->wc_cookie); #ifndef __sparc__ diff --git a/drivers/video/fbdev/aty/radeon_backlight.c b/drivers/video/fbdev/aty/radeon_backlight.c index 427adc838f77..23a38c3f3977 100644 --- a/drivers/video/fbdev/aty/radeon_backlight.c +++ b/drivers/video/fbdev/aty/radeon_backlight.c @@ -147,7 +147,7 @@ void radeonfb_bl_init(struct radeonfb_info *rinfo) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = FB_BACKLIGHT_LEVELS - 1; - bd = backlight_device_register(name, rinfo->info->dev, pdata, + bd = backlight_device_register(name, rinfo->info->device, pdata, &radeon_bl_data, &props); if (IS_ERR(bd)) { rinfo->info->bl_dev = NULL; diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c index 972c4bbedfa3..8f2a527c26eb 100644 --- a/drivers/video/fbdev/aty/radeon_base.c +++ b/drivers/video/fbdev/aty/radeon_base.c @@ -2517,9 +2517,8 @@ static void radeonfb_pci_unregister(struct pci_dev *pdev) del_timer_sync(&rinfo->lvds_timer); arch_phys_wc_del(rinfo->wc_cookie); - unregister_framebuffer(info); - radeonfb_bl_exit(rinfo); + unregister_framebuffer(info); iounmap(rinfo->mmio_base); iounmap(rinfo->fb_base); diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c index 06403a4fe0e3..f7b69d5aa476 100644 --- a/drivers/video/fbdev/broadsheetfb.c +++ b/drivers/video/fbdev/broadsheetfb.c @@ -970,90 +970,28 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info, struct list_head } } -static void broadsheetfb_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) +static void broadsheetfb_defio_damage_range(struct fb_info *info, off_t off, size_t len) { struct broadsheetfb_par *par = info->par; - sys_fillrect(info, rect); - broadsheetfb_dpy_update(par); } -static void broadsheetfb_copyarea(struct fb_info *info, - const struct fb_copyarea *area) +static void broadsheetfb_defio_damage_area(struct fb_info *info, u32 x, u32 y, + u32 width, u32 height) { struct broadsheetfb_par *par = info->par; - sys_copyarea(info, area); - broadsheetfb_dpy_update(par); } -static void broadsheetfb_imageblit(struct fb_info *info, - const struct fb_image *image) -{ - struct broadsheetfb_par *par = info->par; - - sys_imageblit(info, image); - - broadsheetfb_dpy_update(par); -} - -/* - * this is the slow path from userspace. they can seek and write to - * the fb. it's inefficient to do anything less than a full screen draw - */ -static ssize_t broadsheetfb_write(struct fb_info *info, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct broadsheetfb_par *par = info->par; - unsigned long p = *ppos; - void *dst; - int err = 0; - unsigned long total_size; - - if (!info->screen_buffer) - return -ENODEV; - - total_size = info->fix.smem_len; - - if (p > total_size) - return -EFBIG; - - if (count > total_size) { - err = -EFBIG; - count = total_size; - } - - if (count + p > total_size) { - if (!err) - err = -ENOSPC; - - count = total_size - p; - } - - dst = info->screen_buffer + p; - - if (copy_from_user(dst, buf, count)) - err = -EFAULT; - - if (!err) - *ppos += count; - - broadsheetfb_dpy_update(par); - - return (err) ? err : count; -} +FB_GEN_DEFAULT_DEFERRED_SYS_OPS(broadsheetfb, + broadsheetfb_defio_damage_range, + broadsheetfb_defio_damage_area) static const struct fb_ops broadsheetfb_ops = { - .owner = THIS_MODULE, - .fb_read = fb_sys_read, - .fb_write = broadsheetfb_write, - .fb_fillrect = broadsheetfb_fillrect, - .fb_copyarea = broadsheetfb_copyarea, - .fb_imageblit = broadsheetfb_imageblit, - .fb_mmap = fb_deferred_io_mmap, + .owner = THIS_MODULE, + FB_DEFAULT_DEFERRED_OPS(broadsheetfb), }; static struct fb_deferred_io broadsheetfb_defio = { @@ -1200,7 +1138,7 @@ static void broadsheetfb_remove(struct platform_device *dev) if (info) { struct broadsheetfb_par *par = info->par; - device_remove_file(info->dev, &dev_attr_loadstore_waveform); + device_remove_file(info->device, &dev_attr_loadstore_waveform); unregister_framebuffer(info); fb_deferred_io_cleanup(info); par->board->cleanup(par); diff --git a/drivers/video/fbdev/core/Makefile b/drivers/video/fbdev/core/Makefile index 8f0060160ffb..9150bafd9e89 100644 --- a/drivers/video/fbdev/core/Makefile +++ b/drivers/video/fbdev/core/Makefile @@ -1,9 +1,14 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_FB_NOTIFY) += fb_notify.o obj-$(CONFIG_FB) += fb.o -fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ +fb-y := fb_backlight.o \ + fb_info.o \ + fbmem.o fbmon.o fbcmap.o \ modedb.o fbcvt.o fb_cmdline.o fb_io_fops.o fb-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o +fb-$(CONFIG_FB_DEVICE) += fb_chrdev.o \ + fb_procfs.o \ + fbsysfs.o ifeq ($(CONFIG_FRAMEBUFFER_CONSOLE),y) fb-y += fbcon.o bitblit.o softcursor.o diff --git a/drivers/video/fbdev/core/fb_backlight.c b/drivers/video/fbdev/core/fb_backlight.c new file mode 100644 index 000000000000..e2d3b3adc870 --- /dev/null +++ b/drivers/video/fbdev/core/fb_backlight.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/export.h> +#include <linux/fb.h> +#include <linux/mutex.h> + +#if IS_ENABLED(CONFIG_FB_BACKLIGHT) +/* + * This function generates a linear backlight curve + * + * 0: off + * 1-7: min + * 8-127: linear from min to max + */ +void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max) +{ + unsigned int i, flat, count, range = (max - min); + + mutex_lock(&fb_info->bl_curve_mutex); + + fb_info->bl_curve[0] = off; + + for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat) + fb_info->bl_curve[flat] = min; + + count = FB_BACKLIGHT_LEVELS * 15 / 16; + for (i = 0; i < count; ++i) + fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count); + + mutex_unlock(&fb_info->bl_curve_mutex); +} +EXPORT_SYMBOL_GPL(fb_bl_default_curve); +#endif diff --git a/drivers/video/fbdev/core/fb_chrdev.c b/drivers/video/fbdev/core/fb_chrdev.c new file mode 100644 index 000000000000..eadb81f53a82 --- /dev/null +++ b/drivers/video/fbdev/core/fb_chrdev.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/compat.h> +#include <linux/console.h> +#include <linux/fb.h> +#include <linux/fbcon.h> +#include <linux/major.h> + +#include "fb_internal.h" + +/* + * We hold a reference to the fb_info in file->private_data, + * but if the current registered fb has changed, we don't + * actually want to use it. + * + * So look up the fb_info using the inode minor number, + * and just verify it against the reference we have. + */ +static struct fb_info *file_fb_info(struct file *file) +{ + struct inode *inode = file_inode(file); + int fbidx = iminor(inode); + struct fb_info *info = registered_fb[fbidx]; + + if (info != file->private_data) + info = NULL; + return info; +} + +static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct fb_info *info = file_fb_info(file); + + if (!info) + return -ENODEV; + + if (info->state != FBINFO_STATE_RUNNING) + return -EPERM; + + if (info->fbops->fb_read) + return info->fbops->fb_read(info, buf, count, ppos); + + return fb_io_read(info, buf, count, ppos); +} + +static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + struct fb_info *info = file_fb_info(file); + + if (!info) + return -ENODEV; + + if (info->state != FBINFO_STATE_RUNNING) + return -EPERM; + + if (info->fbops->fb_write) + return info->fbops->fb_write(info, buf, count, ppos); + + return fb_io_write(info, buf, count, ppos); +} + +static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + const struct fb_ops *fb; + struct fb_var_screeninfo var; + struct fb_fix_screeninfo fix; + struct fb_cmap cmap_from; + struct fb_cmap_user cmap; + void __user *argp = (void __user *)arg; + long ret = 0; + + switch (cmd) { + case FBIOGET_VSCREENINFO: + lock_fb_info(info); + var = info->var; + unlock_fb_info(info); + + ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; + break; + case FBIOPUT_VSCREENINFO: + if (copy_from_user(&var, argp, sizeof(var))) + return -EFAULT; + /* only for kernel-internal use */ + var.activate &= ~FB_ACTIVATE_KD_TEXT; + console_lock(); + lock_fb_info(info); + ret = fbcon_modechange_possible(info, &var); + if (!ret) + ret = fb_set_var(info, &var); + if (!ret) + fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL); + unlock_fb_info(info); + console_unlock(); + if (!ret && copy_to_user(argp, &var, sizeof(var))) + ret = -EFAULT; + break; + case FBIOGET_FSCREENINFO: + lock_fb_info(info); + memcpy(&fix, &info->fix, sizeof(fix)); + if (info->flags & FBINFO_HIDE_SMEM_START) + fix.smem_start = 0; + unlock_fb_info(info); + + ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0; + break; + case FBIOPUTCMAP: + if (copy_from_user(&cmap, argp, sizeof(cmap))) + return -EFAULT; + ret = fb_set_user_cmap(&cmap, info); + break; + case FBIOGETCMAP: + if (copy_from_user(&cmap, argp, sizeof(cmap))) + return -EFAULT; + lock_fb_info(info); + cmap_from = info->cmap; + unlock_fb_info(info); + ret = fb_cmap_to_user(&cmap_from, &cmap); + break; + case FBIOPAN_DISPLAY: + if (copy_from_user(&var, argp, sizeof(var))) + return -EFAULT; + console_lock(); + lock_fb_info(info); + ret = fb_pan_display(info, &var); + unlock_fb_info(info); + console_unlock(); + if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) + return -EFAULT; + break; + case FBIO_CURSOR: + ret = -EINVAL; + break; + case FBIOGET_CON2FBMAP: + ret = fbcon_get_con2fb_map_ioctl(argp); + break; + case FBIOPUT_CON2FBMAP: + ret = fbcon_set_con2fb_map_ioctl(argp); + break; + case FBIOBLANK: + if (arg > FB_BLANK_POWERDOWN) + return -EINVAL; + console_lock(); + lock_fb_info(info); + ret = fb_blank(info, arg); + /* might again call into fb_blank */ + fbcon_fb_blanked(info, arg); + unlock_fb_info(info); + console_unlock(); + break; + default: + lock_fb_info(info); + fb = info->fbops; + if (fb->fb_ioctl) + ret = fb->fb_ioctl(info, cmd, arg); + else + ret = -ENOTTY; + unlock_fb_info(info); + } + return ret; +} + +static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct fb_info *info = file_fb_info(file); + + if (!info) + return -ENODEV; + return do_fb_ioctl(info, cmd, arg); +} + +#ifdef CONFIG_COMPAT +struct fb_fix_screeninfo32 { + char id[16]; + compat_caddr_t smem_start; + u32 smem_len; + u32 type; + u32 type_aux; + u32 visual; + u16 xpanstep; + u16 ypanstep; + u16 ywrapstep; + u32 line_length; + compat_caddr_t mmio_start; + u32 mmio_len; + u32 accel; + u16 reserved[3]; +}; + +struct fb_cmap32 { + u32 start; + u32 len; + compat_caddr_t red; + compat_caddr_t green; + compat_caddr_t blue; + compat_caddr_t transp; +}; + +static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct fb_cmap32 cmap32; + struct fb_cmap cmap_from; + struct fb_cmap_user cmap; + + if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32))) + return -EFAULT; + + cmap = (struct fb_cmap_user) { + .start = cmap32.start, + .len = cmap32.len, + .red = compat_ptr(cmap32.red), + .green = compat_ptr(cmap32.green), + .blue = compat_ptr(cmap32.blue), + .transp = compat_ptr(cmap32.transp), + }; + + if (cmd == FBIOPUTCMAP) + return fb_set_user_cmap(&cmap, info); + + lock_fb_info(info); + cmap_from = info->cmap; + unlock_fb_info(info); + + return fb_cmap_to_user(&cmap_from, &cmap); +} + +static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, + struct fb_fix_screeninfo32 __user *fix32) +{ + __u32 data; + int err; + + err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id)); + + data = (__u32) (unsigned long) fix->smem_start; + err |= put_user(data, &fix32->smem_start); + + err |= put_user(fix->smem_len, &fix32->smem_len); + err |= put_user(fix->type, &fix32->type); + err |= put_user(fix->type_aux, &fix32->type_aux); + err |= put_user(fix->visual, &fix32->visual); + err |= put_user(fix->xpanstep, &fix32->xpanstep); + err |= put_user(fix->ypanstep, &fix32->ypanstep); + err |= put_user(fix->ywrapstep, &fix32->ywrapstep); + err |= put_user(fix->line_length, &fix32->line_length); + + data = (__u32) (unsigned long) fix->mmio_start; + err |= put_user(data, &fix32->mmio_start); + + err |= put_user(fix->mmio_len, &fix32->mmio_len); + err |= put_user(fix->accel, &fix32->accel); + err |= copy_to_user(fix32->reserved, fix->reserved, + sizeof(fix->reserved)); + + if (err) + return -EFAULT; + return 0; +} + +static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct fb_fix_screeninfo fix; + + lock_fb_info(info); + fix = info->fix; + if (info->flags & FBINFO_HIDE_SMEM_START) + fix.smem_start = 0; + unlock_fb_info(info); + return do_fscreeninfo_to_user(&fix, compat_ptr(arg)); +} + +static long fb_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct fb_info *info = file_fb_info(file); + const struct fb_ops *fb; + long ret = -ENOIOCTLCMD; + + if (!info) + return -ENODEV; + fb = info->fbops; + switch (cmd) { + case FBIOGET_VSCREENINFO: + case FBIOPUT_VSCREENINFO: + case FBIOPAN_DISPLAY: + case FBIOGET_CON2FBMAP: + case FBIOPUT_CON2FBMAP: + arg = (unsigned long) compat_ptr(arg); + fallthrough; + case FBIOBLANK: + ret = do_fb_ioctl(info, cmd, arg); + break; + + case FBIOGET_FSCREENINFO: + ret = fb_get_fscreeninfo(info, cmd, arg); + break; + + case FBIOGETCMAP: + case FBIOPUTCMAP: + ret = fb_getput_cmap(info, cmd, arg); + break; + + default: + if (fb->fb_compat_ioctl) + ret = fb->fb_compat_ioctl(info, cmd, arg); + break; + } + return ret; +} +#endif + +static int fb_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct fb_info *info = file_fb_info(file); + unsigned long mmio_pgoff; + unsigned long start; + u32 len; + + if (!info) + return -ENODEV; + mutex_lock(&info->mm_lock); + + if (info->fbops->fb_mmap) { + int res; + + /* + * The framebuffer needs to be accessed decrypted, be sure + * SME protection is removed ahead of the call + */ + vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); + res = info->fbops->fb_mmap(info, vma); + mutex_unlock(&info->mm_lock); + return res; +#if IS_ENABLED(CONFIG_FB_DEFERRED_IO) + } else if (info->fbdefio) { + /* + * FB deferred I/O wants you to handle mmap in your drivers. At a + * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap(). + */ + dev_warn_once(info->dev, "fbdev mmap not set up for deferred I/O.\n"); + mutex_unlock(&info->mm_lock); + return -ENODEV; +#endif + } + + /* + * Ugh. This can be either the frame buffer mapping, or + * if pgoff points past it, the mmio mapping. + */ + start = info->fix.smem_start; + len = info->fix.smem_len; + mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT; + if (vma->vm_pgoff >= mmio_pgoff) { + if (info->var.accel_flags) { + mutex_unlock(&info->mm_lock); + return -EINVAL; + } + + vma->vm_pgoff -= mmio_pgoff; + start = info->fix.mmio_start; + len = info->fix.mmio_len; + } + mutex_unlock(&info->mm_lock); + + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); + fb_pgprotect(file, vma, start); + + return vm_iomap_memory(vma, start, len); +} + +static int fb_open(struct inode *inode, struct file *file) +__acquires(&info->lock) +__releases(&info->lock) +{ + int fbidx = iminor(inode); + struct fb_info *info; + int res = 0; + + info = get_fb_info(fbidx); + if (!info) { + request_module("fb%d", fbidx); + info = get_fb_info(fbidx); + if (!info) + return -ENODEV; + } + if (IS_ERR(info)) + return PTR_ERR(info); + + lock_fb_info(info); + if (!try_module_get(info->fbops->owner)) { + res = -ENODEV; + goto out; + } + file->private_data = info; + if (info->fbops->fb_open) { + res = info->fbops->fb_open(info, 1); + if (res) + module_put(info->fbops->owner); + } +#ifdef CONFIG_FB_DEFERRED_IO + if (info->fbdefio) + fb_deferred_io_open(info, inode, file); +#endif +out: + unlock_fb_info(info); + if (res) + put_fb_info(info); + return res; +} + +static int fb_release(struct inode *inode, struct file *file) +__acquires(&info->lock) +__releases(&info->lock) +{ + struct fb_info * const info = file->private_data; + + lock_fb_info(info); +#if IS_ENABLED(CONFIG_FB_DEFERRED_IO) + if (info->fbdefio) + fb_deferred_io_release(info); +#endif + if (info->fbops->fb_release) + info->fbops->fb_release(info, 1); + module_put(info->fbops->owner); + unlock_fb_info(info); + put_fb_info(info); + return 0; +} + +#if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU) +static unsigned long get_fb_unmapped_area(struct file *filp, + unsigned long addr, unsigned long len, + unsigned long pgoff, unsigned long flags) +{ + struct fb_info * const info = filp->private_data; + unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len); + + if (pgoff > fb_size || len > fb_size - pgoff) + return -EINVAL; + + return (unsigned long)info->screen_base + pgoff; +} +#endif + +static const struct file_operations fb_fops = { + .owner = THIS_MODULE, + .read = fb_read, + .write = fb_write, + .unlocked_ioctl = fb_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = fb_compat_ioctl, +#endif + .mmap = fb_mmap, + .open = fb_open, + .release = fb_release, +#if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \ + (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \ + !defined(CONFIG_MMU)) + .get_unmapped_area = get_fb_unmapped_area, +#endif +#ifdef CONFIG_FB_DEFERRED_IO + .fsync = fb_deferred_io_fsync, +#endif + .llseek = default_llseek, +}; + +int fb_register_chrdev(void) +{ + int ret; + + ret = register_chrdev(FB_MAJOR, "fb", &fb_fops); + if (ret) { + pr_err("Unable to get major %d for fb devs\n", FB_MAJOR); + return ret; + } + + return ret; +} + +void fb_unregister_chrdev(void) +{ + unregister_chrdev(FB_MAJOR, "fb"); +} diff --git a/drivers/video/fbdev/core/fb_info.c b/drivers/video/fbdev/core/fb_info.c new file mode 100644 index 000000000000..8bdbefdd4b70 --- /dev/null +++ b/drivers/video/fbdev/core/fb_info.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/export.h> +#include <linux/fb.h> +#include <linux/mutex.h> +#include <linux/slab.h> + +/** + * framebuffer_alloc - creates a new frame buffer info structure + * + * @size: size of driver private data, can be zero + * @dev: pointer to the device for this fb, this can be NULL + * + * Creates a new frame buffer info structure. Also reserves @size bytes + * for driver private data (info->par). info->par (if any) will be + * aligned to sizeof(long). + * + * Returns the new structure, or NULL if an error occurred. + * + */ +struct fb_info *framebuffer_alloc(size_t size, struct device *dev) +{ +#define BYTES_PER_LONG (BITS_PER_LONG/8) +#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) + int fb_info_size = sizeof(struct fb_info); + struct fb_info *info; + char *p; + + if (size) + fb_info_size += PADDING; + + p = kzalloc(fb_info_size + size, GFP_KERNEL); + + if (!p) + return NULL; + + info = (struct fb_info *) p; + + if (size) + info->par = p + fb_info_size; + + info->device = dev; + info->fbcon_rotate_hint = -1; + +#if IS_ENABLED(CONFIG_FB_BACKLIGHT) + mutex_init(&info->bl_curve_mutex); +#endif + + return info; +#undef PADDING +#undef BYTES_PER_LONG +} +EXPORT_SYMBOL(framebuffer_alloc); + +/** + * framebuffer_release - marks the structure available for freeing + * + * @info: frame buffer info structure + * + * Drop the reference count of the device embedded in the + * framebuffer info structure. + * + */ +void framebuffer_release(struct fb_info *info) +{ + if (!info) + return; + + if (WARN_ON(refcount_read(&info->count))) + return; + +#if IS_ENABLED(CONFIG_FB_BACKLIGHT) + mutex_destroy(&info->bl_curve_mutex); +#endif + + kfree(info); +} +EXPORT_SYMBOL(framebuffer_release); diff --git a/drivers/video/fbdev/core/fb_internal.h b/drivers/video/fbdev/core/fb_internal.h new file mode 100644 index 000000000000..4c8d509a0026 --- /dev/null +++ b/drivers/video/fbdev/core/fb_internal.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _FB_INTERNAL_H +#define _FB_INTERNAL_H + +#include <linux/device.h> +#include <linux/fb.h> +#include <linux/mutex.h> + +/* fb_devfs.c */ +#if defined(CONFIG_FB_DEVICE) +int fb_register_chrdev(void); +void fb_unregister_chrdev(void); +#else +static inline int fb_register_chrdev(void) +{ + return 0; +} +static inline void fb_unregister_chrdev(void) +{ } +#endif + +/* fbmem.c */ +extern struct class *fb_class; +extern struct mutex registration_lock; +extern struct fb_info *registered_fb[FB_MAX]; +extern int num_registered_fb; +struct fb_info *get_fb_info(unsigned int idx); +void put_fb_info(struct fb_info *fb_info); + +/* fb_procfs.c */ +#if defined(CONFIG_FB_DEVICE) +int fb_init_procfs(void); +void fb_cleanup_procfs(void); +#else +static inline int fb_init_procfs(void) +{ + return 0; +} +static inline void fb_cleanup_procfs(void) +{ } +#endif + +/* fbsysfs.c */ +#if defined(CONFIG_FB_DEVICE) +int fb_device_create(struct fb_info *fb_info); +void fb_device_destroy(struct fb_info *fb_info); +#else +static inline int fb_device_create(struct fb_info *fb_info) +{ + /* + * Acquire a reference on the parent device to avoid + * unplug operations behind our back. With the fbdev + * device enabled, this is performed within register_device(). + */ + get_device(fb_info->device); + + return 0; +} +static inline void fb_device_destroy(struct fb_info *fb_info) +{ + /* Undo the get_device() from fb_device_create() */ + put_device(fb_info->device); +} +#endif + +#endif diff --git a/drivers/video/fbdev/core/fb_procfs.c b/drivers/video/fbdev/core/fb_procfs.c new file mode 100644 index 000000000000..59641142f8aa --- /dev/null +++ b/drivers/video/fbdev/core/fb_procfs.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/proc_fs.h> + +#include "fb_internal.h" + +static struct proc_dir_entry *fb_proc_dir_entry; + +static void *fb_seq_start(struct seq_file *m, loff_t *pos) +{ + mutex_lock(®istration_lock); + + return (*pos < FB_MAX) ? pos : NULL; +} + +static void fb_seq_stop(struct seq_file *m, void *v) +{ + mutex_unlock(®istration_lock); +} + +static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + (*pos)++; + + return (*pos < FB_MAX) ? pos : NULL; +} + +static int fb_seq_show(struct seq_file *m, void *v) +{ + int i = *(loff_t *)v; + struct fb_info *fi = registered_fb[i]; + + if (fi) + seq_printf(m, "%d %s\n", fi->node, fi->fix.id); + + return 0; +} + +static const struct seq_operations __maybe_unused fb_proc_seq_ops = { + .start = fb_seq_start, + .stop = fb_seq_stop, + .next = fb_seq_next, + .show = fb_seq_show, +}; + +int fb_init_procfs(void) +{ + struct proc_dir_entry *proc; + + proc = proc_create_seq("fb", 0, NULL, &fb_proc_seq_ops); + if (!proc) + return -ENOMEM; + + fb_proc_dir_entry = proc; + + return 0; +} + +void fb_cleanup_procfs(void) +{ + proc_remove(fb_proc_dir_entry); +} diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index c6c9d040bdec..8e76bc246b38 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -78,6 +78,7 @@ #include <asm/irq.h> #include "fbcon.h" +#include "fb_internal.h" /* * FIXME: Locking diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 329d16e49a90..ee44a46a66be 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -13,20 +13,16 @@ #include <linux/module.h> -#include <linux/compat.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/kernel.h> -#include <linux/major.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/vt.h> #include <linux/init.h> #include <linux/linux_logo.h> -#include <linux/proc_fs.h> #include <linux/platform_device.h> -#include <linux/seq_file.h> #include <linux/console.h> #include <linux/kmod.h> #include <linux/err.h> @@ -40,14 +36,17 @@ #include <video/nomodeset.h> #include <video/vga.h> +#include "fb_internal.h" + /* * Frame buffer device initialization and setup routines */ #define FBPIXMAPSIZE (1024 * 8) -static DEFINE_MUTEX(registration_lock); +struct class *fb_class; +DEFINE_MUTEX(registration_lock); struct fb_info *registered_fb[FB_MAX] __read_mostly; int num_registered_fb __read_mostly; #define for_each_registered_fb(i) \ @@ -58,7 +57,7 @@ bool fb_center_logo __read_mostly; int fb_logo_count __read_mostly = -1; -static struct fb_info *get_fb_info(unsigned int idx) +struct fb_info *get_fb_info(unsigned int idx) { struct fb_info *fb_info; @@ -74,7 +73,7 @@ static struct fb_info *get_fb_info(unsigned int idx) return fb_info; } -static void put_fb_info(struct fb_info *fb_info) +void put_fb_info(struct fb_info *fb_info) { if (!refcount_dec_and_test(&fb_info->count)) return; @@ -703,93 +702,6 @@ int fb_show_logo(struct fb_info *info, int rotate) { return 0; } EXPORT_SYMBOL(fb_prepare_logo); EXPORT_SYMBOL(fb_show_logo); -static void *fb_seq_start(struct seq_file *m, loff_t *pos) -{ - mutex_lock(®istration_lock); - return (*pos < FB_MAX) ? pos : NULL; -} - -static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos) -{ - (*pos)++; - return (*pos < FB_MAX) ? pos : NULL; -} - -static void fb_seq_stop(struct seq_file *m, void *v) -{ - mutex_unlock(®istration_lock); -} - -static int fb_seq_show(struct seq_file *m, void *v) -{ - int i = *(loff_t *)v; - struct fb_info *fi = registered_fb[i]; - - if (fi) - seq_printf(m, "%d %s\n", fi->node, fi->fix.id); - return 0; -} - -static const struct seq_operations __maybe_unused proc_fb_seq_ops = { - .start = fb_seq_start, - .next = fb_seq_next, - .stop = fb_seq_stop, - .show = fb_seq_show, -}; - -/* - * We hold a reference to the fb_info in file->private_data, - * but if the current registered fb has changed, we don't - * actually want to use it. - * - * So look up the fb_info using the inode minor number, - * and just verify it against the reference we have. - */ -static struct fb_info *file_fb_info(struct file *file) -{ - struct inode *inode = file_inode(file); - int fbidx = iminor(inode); - struct fb_info *info = registered_fb[fbidx]; - - if (info != file->private_data) - info = NULL; - return info; -} - -static ssize_t -fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - struct fb_info *info = file_fb_info(file); - - if (!info) - return -ENODEV; - - if (info->state != FBINFO_STATE_RUNNING) - return -EPERM; - - if (info->fbops->fb_read) - return info->fbops->fb_read(info, buf, count, ppos); - - return fb_io_read(info, buf, count, ppos); -} - -static ssize_t -fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - struct fb_info *info = file_fb_info(file); - - if (!info) - return -ENODEV; - - if (info->state != FBINFO_STATE_RUNNING) - return -EPERM; - - if (info->fbops->fb_write) - return info->fbops->fb_write(info, buf, count, ppos); - - return fb_io_write(info, buf, count, ppos); -} - int fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) { @@ -989,419 +901,6 @@ fb_blank(struct fb_info *info, int blank) } EXPORT_SYMBOL(fb_blank); -static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, - unsigned long arg) -{ - const struct fb_ops *fb; - struct fb_var_screeninfo var; - struct fb_fix_screeninfo fix; - struct fb_cmap cmap_from; - struct fb_cmap_user cmap; - void __user *argp = (void __user *)arg; - long ret = 0; - - switch (cmd) { - case FBIOGET_VSCREENINFO: - lock_fb_info(info); - var = info->var; - unlock_fb_info(info); - - ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; - break; - case FBIOPUT_VSCREENINFO: - if (copy_from_user(&var, argp, sizeof(var))) - return -EFAULT; - /* only for kernel-internal use */ - var.activate &= ~FB_ACTIVATE_KD_TEXT; - console_lock(); - lock_fb_info(info); - ret = fbcon_modechange_possible(info, &var); - if (!ret) - ret = fb_set_var(info, &var); - if (!ret) - fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL); - unlock_fb_info(info); - console_unlock(); - if (!ret && copy_to_user(argp, &var, sizeof(var))) - ret = -EFAULT; - break; - case FBIOGET_FSCREENINFO: - lock_fb_info(info); - memcpy(&fix, &info->fix, sizeof(fix)); - if (info->flags & FBINFO_HIDE_SMEM_START) - fix.smem_start = 0; - unlock_fb_info(info); - - ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0; - break; - case FBIOPUTCMAP: - if (copy_from_user(&cmap, argp, sizeof(cmap))) - return -EFAULT; - ret = fb_set_user_cmap(&cmap, info); - break; - case FBIOGETCMAP: - if (copy_from_user(&cmap, argp, sizeof(cmap))) - return -EFAULT; - lock_fb_info(info); - cmap_from = info->cmap; - unlock_fb_info(info); - ret = fb_cmap_to_user(&cmap_from, &cmap); - break; - case FBIOPAN_DISPLAY: - if (copy_from_user(&var, argp, sizeof(var))) - return -EFAULT; - console_lock(); - lock_fb_info(info); - ret = fb_pan_display(info, &var); - unlock_fb_info(info); - console_unlock(); - if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) - return -EFAULT; - break; - case FBIO_CURSOR: - ret = -EINVAL; - break; - case FBIOGET_CON2FBMAP: - ret = fbcon_get_con2fb_map_ioctl(argp); - break; - case FBIOPUT_CON2FBMAP: - ret = fbcon_set_con2fb_map_ioctl(argp); - break; - case FBIOBLANK: - if (arg > FB_BLANK_POWERDOWN) - return -EINVAL; - console_lock(); - lock_fb_info(info); - ret = fb_blank(info, arg); - /* might again call into fb_blank */ - fbcon_fb_blanked(info, arg); - unlock_fb_info(info); - console_unlock(); - break; - default: - lock_fb_info(info); - fb = info->fbops; - if (fb->fb_ioctl) - ret = fb->fb_ioctl(info, cmd, arg); - else - ret = -ENOTTY; - unlock_fb_info(info); - } - return ret; -} - -static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct fb_info *info = file_fb_info(file); - - if (!info) - return -ENODEV; - return do_fb_ioctl(info, cmd, arg); -} - -#ifdef CONFIG_COMPAT -struct fb_fix_screeninfo32 { - char id[16]; - compat_caddr_t smem_start; - u32 smem_len; - u32 type; - u32 type_aux; - u32 visual; - u16 xpanstep; - u16 ypanstep; - u16 ywrapstep; - u32 line_length; - compat_caddr_t mmio_start; - u32 mmio_len; - u32 accel; - u16 reserved[3]; -}; - -struct fb_cmap32 { - u32 start; - u32 len; - compat_caddr_t red; - compat_caddr_t green; - compat_caddr_t blue; - compat_caddr_t transp; -}; - -static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, - unsigned long arg) -{ - struct fb_cmap32 cmap32; - struct fb_cmap cmap_from; - struct fb_cmap_user cmap; - - if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32))) - return -EFAULT; - - cmap = (struct fb_cmap_user) { - .start = cmap32.start, - .len = cmap32.len, - .red = compat_ptr(cmap32.red), - .green = compat_ptr(cmap32.green), - .blue = compat_ptr(cmap32.blue), - .transp = compat_ptr(cmap32.transp), - }; - - if (cmd == FBIOPUTCMAP) - return fb_set_user_cmap(&cmap, info); - - lock_fb_info(info); - cmap_from = info->cmap; - unlock_fb_info(info); - - return fb_cmap_to_user(&cmap_from, &cmap); -} - -static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, - struct fb_fix_screeninfo32 __user *fix32) -{ - __u32 data; - int err; - - err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id)); - - data = (__u32) (unsigned long) fix->smem_start; - err |= put_user(data, &fix32->smem_start); - - err |= put_user(fix->smem_len, &fix32->smem_len); - err |= put_user(fix->type, &fix32->type); - err |= put_user(fix->type_aux, &fix32->type_aux); - err |= put_user(fix->visual, &fix32->visual); - err |= put_user(fix->xpanstep, &fix32->xpanstep); - err |= put_user(fix->ypanstep, &fix32->ypanstep); - err |= put_user(fix->ywrapstep, &fix32->ywrapstep); - err |= put_user(fix->line_length, &fix32->line_length); - - data = (__u32) (unsigned long) fix->mmio_start; - err |= put_user(data, &fix32->mmio_start); - - err |= put_user(fix->mmio_len, &fix32->mmio_len); - err |= put_user(fix->accel, &fix32->accel); - err |= copy_to_user(fix32->reserved, fix->reserved, - sizeof(fix->reserved)); - - if (err) - return -EFAULT; - return 0; -} - -static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, - unsigned long arg) -{ - struct fb_fix_screeninfo fix; - - lock_fb_info(info); - fix = info->fix; - if (info->flags & FBINFO_HIDE_SMEM_START) - fix.smem_start = 0; - unlock_fb_info(info); - return do_fscreeninfo_to_user(&fix, compat_ptr(arg)); -} - -static long fb_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct fb_info *info = file_fb_info(file); - const struct fb_ops *fb; - long ret = -ENOIOCTLCMD; - - if (!info) - return -ENODEV; - fb = info->fbops; - switch(cmd) { - case FBIOGET_VSCREENINFO: - case FBIOPUT_VSCREENINFO: - case FBIOPAN_DISPLAY: - case FBIOGET_CON2FBMAP: - case FBIOPUT_CON2FBMAP: - arg = (unsigned long) compat_ptr(arg); - fallthrough; - case FBIOBLANK: - ret = do_fb_ioctl(info, cmd, arg); - break; - - case FBIOGET_FSCREENINFO: - ret = fb_get_fscreeninfo(info, cmd, arg); - break; - - case FBIOGETCMAP: - case FBIOPUTCMAP: - ret = fb_getput_cmap(info, cmd, arg); - break; - - default: - if (fb->fb_compat_ioctl) - ret = fb->fb_compat_ioctl(info, cmd, arg); - break; - } - return ret; -} -#endif - -static int -fb_mmap(struct file *file, struct vm_area_struct * vma) -{ - struct fb_info *info = file_fb_info(file); - unsigned long mmio_pgoff; - unsigned long start; - u32 len; - - if (!info) - return -ENODEV; - mutex_lock(&info->mm_lock); - - if (info->fbops->fb_mmap) { - int res; - - /* - * The framebuffer needs to be accessed decrypted, be sure - * SME protection is removed ahead of the call - */ - vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); - res = info->fbops->fb_mmap(info, vma); - mutex_unlock(&info->mm_lock); - return res; -#if IS_ENABLED(CONFIG_FB_DEFERRED_IO) - } else if (info->fbdefio) { - /* - * FB deferred I/O wants you to handle mmap in your drivers. At a - * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap(). - */ - dev_warn_once(info->dev, "fbdev mmap not set up for deferred I/O.\n"); - mutex_unlock(&info->mm_lock); - return -ENODEV; -#endif - } - - /* - * Ugh. This can be either the frame buffer mapping, or - * if pgoff points past it, the mmio mapping. - */ - start = info->fix.smem_start; - len = info->fix.smem_len; - mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT; - if (vma->vm_pgoff >= mmio_pgoff) { - if (info->var.accel_flags) { - mutex_unlock(&info->mm_lock); - return -EINVAL; - } - - vma->vm_pgoff -= mmio_pgoff; - start = info->fix.mmio_start; - len = info->fix.mmio_len; - } - mutex_unlock(&info->mm_lock); - - vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); - fb_pgprotect(file, vma, start); - - return vm_iomap_memory(vma, start, len); -} - -static int -fb_open(struct inode *inode, struct file *file) -__acquires(&info->lock) -__releases(&info->lock) -{ - int fbidx = iminor(inode); - struct fb_info *info; - int res = 0; - - info = get_fb_info(fbidx); - if (!info) { - request_module("fb%d", fbidx); - info = get_fb_info(fbidx); - if (!info) - return -ENODEV; - } - if (IS_ERR(info)) - return PTR_ERR(info); - - lock_fb_info(info); - if (!try_module_get(info->fbops->owner)) { - res = -ENODEV; - goto out; - } - file->private_data = info; - if (info->fbops->fb_open) { - res = info->fbops->fb_open(info,1); - if (res) - module_put(info->fbops->owner); - } -#ifdef CONFIG_FB_DEFERRED_IO - if (info->fbdefio) - fb_deferred_io_open(info, inode, file); -#endif -out: - unlock_fb_info(info); - if (res) - put_fb_info(info); - return res; -} - -static int -fb_release(struct inode *inode, struct file *file) -__acquires(&info->lock) -__releases(&info->lock) -{ - struct fb_info * const info = file->private_data; - - lock_fb_info(info); -#if IS_ENABLED(CONFIG_FB_DEFERRED_IO) - if (info->fbdefio) - fb_deferred_io_release(info); -#endif - if (info->fbops->fb_release) - info->fbops->fb_release(info,1); - module_put(info->fbops->owner); - unlock_fb_info(info); - put_fb_info(info); - return 0; -} - -#if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU) -static unsigned long get_fb_unmapped_area(struct file *filp, - unsigned long addr, unsigned long len, - unsigned long pgoff, unsigned long flags) -{ - struct fb_info * const info = filp->private_data; - unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len); - - if (pgoff > fb_size || len > fb_size - pgoff) - return -EINVAL; - - return (unsigned long)info->screen_base + pgoff; -} -#endif - -static const struct file_operations fb_fops = { - .owner = THIS_MODULE, - .read = fb_read, - .write = fb_write, - .unlocked_ioctl = fb_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = fb_compat_ioctl, -#endif - .mmap = fb_mmap, - .open = fb_open, - .release = fb_release, -#if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \ - (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \ - !defined(CONFIG_MMU)) - .get_unmapped_area = get_fb_unmapped_area, -#endif -#ifdef CONFIG_FB_DEFERRED_IO - .fsync = fb_deferred_io_fsync, -#endif - .llseek = default_llseek, -}; - -struct class *fb_class; -EXPORT_SYMBOL(fb_class); - static int fb_check_foreignness(struct fb_info *fi) { const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN; @@ -1447,14 +946,7 @@ static int do_register_framebuffer(struct fb_info *fb_info) mutex_init(&fb_info->lock); mutex_init(&fb_info->mm_lock); - fb_info->dev = device_create(fb_class, fb_info->device, - MKDEV(FB_MAJOR, i), NULL, "fb%d", i); - if (IS_ERR(fb_info->dev)) { - /* Not fatal */ - printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev)); - fb_info->dev = NULL; - } else - fb_init_device(fb_info); + fb_device_create(fb_info); if (fb_info->pixmap.addr == NULL) { fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); @@ -1478,9 +970,9 @@ static int do_register_framebuffer(struct fb_info *fb_info) INIT_LIST_HEAD(&fb_info->modelist); if (fb_info->skip_vt_switch) - pm_vt_switch_required(fb_info->dev, false); + pm_vt_switch_required(fb_info->device, false); else - pm_vt_switch_required(fb_info->dev, true); + pm_vt_switch_required(fb_info->device, true); fb_var_to_videomode(&mode, &fb_info->var); fb_add_videomode(&mode, &fb_info->modelist); @@ -1515,16 +1007,9 @@ static void unlink_framebuffer(struct fb_info *fb_info) if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)) return; - if (!fb_info->dev) - return; - - device_destroy(fb_class, MKDEV(FB_MAJOR, i)); - - pm_vt_switch_unregister(fb_info->dev); - + fb_device_destroy(fb_info); + pm_vt_switch_unregister(fb_info->device); unbind_console(fb_info); - - fb_info->dev = NULL; } static void do_unregister_framebuffer(struct fb_info *fb_info) @@ -1539,7 +1024,6 @@ static void do_unregister_framebuffer(struct fb_info *fb_info) fb_destroy_modelist(&fb_info->modelist); registered_fb[fb_info->node] = NULL; num_registered_fb--; - fb_cleanup_device(fb_info); #ifdef CONFIG_GUMSTIX_AM200EPD { struct fb_event event; @@ -1623,60 +1107,48 @@ void fb_set_suspend(struct fb_info *info, int state) } EXPORT_SYMBOL(fb_set_suspend); -/** - * fbmem_init - init frame buffer subsystem - * - * Initialize the frame buffer subsystem. - * - * NOTE: This function is _only_ to be called by drivers/char/mem.c. - * - */ - -static int __init -fbmem_init(void) +static int __init fbmem_init(void) { int ret; - if (!proc_create_seq("fb", 0, NULL, &proc_fb_seq_ops)) - return -ENOMEM; - - ret = register_chrdev(FB_MAJOR, "fb", &fb_fops); - if (ret) { - printk("unable to get major %d for fb devs\n", FB_MAJOR); - goto err_chrdev; - } - fb_class = class_create("graphics"); if (IS_ERR(fb_class)) { ret = PTR_ERR(fb_class); - pr_warn("Unable to create fb class; errno = %d\n", ret); - fb_class = NULL; - goto err_class; + pr_err("Unable to create fb class; errno = %d\n", ret); + goto err_fb_class; } + ret = fb_init_procfs(); + if (ret) + goto err_class_destroy; + + ret = fb_register_chrdev(); + if (ret) + goto err_fb_cleanup_procfs; + fb_console_init(); return 0; -err_class: - unregister_chrdev(FB_MAJOR, "fb"); -err_chrdev: - remove_proc_entry("fb", NULL); +err_fb_cleanup_procfs: + fb_cleanup_procfs(); +err_class_destroy: + class_destroy(fb_class); +err_fb_class: + fb_class = NULL; return ret; } #ifdef MODULE -module_init(fbmem_init); -static void __exit -fbmem_exit(void) +static void __exit fbmem_exit(void) { fb_console_exit(); - - remove_proc_entry("fb", NULL); + fb_unregister_chrdev(); + fb_cleanup_procfs(); class_destroy(fb_class); - unregister_chrdev(FB_MAJOR, "fb"); } +module_init(fbmem_init); module_exit(fbmem_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Framebuffer base"); diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index 0c33c4adcd79..fafe574398b0 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -5,92 +5,14 @@ * Copyright (c) 2004 James Simmons <jsimmons@infradead.org> */ -/* - * Note: currently there's only stubs for framebuffer_alloc and - * framebuffer_release here. The reson for that is that until all drivers - * are converted to use it a sysfsification will open OOPSable races. - */ - -#include <linux/kernel.h> -#include <linux/slab.h> +#include <linux/console.h> #include <linux/fb.h> #include <linux/fbcon.h> -#include <linux/console.h> -#include <linux/module.h> - -#define FB_SYSFS_FLAG_ATTR 1 - -/** - * framebuffer_alloc - creates a new frame buffer info structure - * - * @size: size of driver private data, can be zero - * @dev: pointer to the device for this fb, this can be NULL - * - * Creates a new frame buffer info structure. Also reserves @size bytes - * for driver private data (info->par). info->par (if any) will be - * aligned to sizeof(long). - * - * Returns the new structure, or NULL if an error occurred. - * - */ -struct fb_info *framebuffer_alloc(size_t size, struct device *dev) -{ -#define BYTES_PER_LONG (BITS_PER_LONG/8) -#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) - int fb_info_size = sizeof(struct fb_info); - struct fb_info *info; - char *p; - - if (size) - fb_info_size += PADDING; - - p = kzalloc(fb_info_size + size, GFP_KERNEL); - - if (!p) - return NULL; - - info = (struct fb_info *) p; - - if (size) - info->par = p + fb_info_size; - - info->device = dev; - info->fbcon_rotate_hint = -1; - -#if IS_ENABLED(CONFIG_FB_BACKLIGHT) - mutex_init(&info->bl_curve_mutex); -#endif - - return info; -#undef PADDING -#undef BYTES_PER_LONG -} -EXPORT_SYMBOL(framebuffer_alloc); +#include <linux/major.h> -/** - * framebuffer_release - marks the structure available for freeing - * - * @info: frame buffer info structure - * - * Drop the reference count of the device embedded in the - * framebuffer info structure. - * - */ -void framebuffer_release(struct fb_info *info) -{ - if (!info) - return; +#include "fb_internal.h" - if (WARN_ON(refcount_read(&info->count))) - return; - -#if IS_ENABLED(CONFIG_FB_BACKLIGHT) - mutex_destroy(&info->bl_curve_mutex); -#endif - - kfree(info); -} -EXPORT_SYMBOL(framebuffer_release); +#define FB_SYSFS_FLAG_ATTR 1 static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) { @@ -516,7 +438,7 @@ static struct device_attribute device_attrs[] = { #endif }; -int fb_init_device(struct fb_info *fb_info) +static int fb_init_device(struct fb_info *fb_info) { int i, error = 0; @@ -540,7 +462,7 @@ int fb_init_device(struct fb_info *fb_info) return 0; } -void fb_cleanup_device(struct fb_info *fb_info) +static void fb_cleanup_device(struct fb_info *fb_info) { unsigned int i; @@ -552,29 +474,33 @@ void fb_cleanup_device(struct fb_info *fb_info) } } -#if IS_ENABLED(CONFIG_FB_BACKLIGHT) -/* This function generates a linear backlight curve - * - * 0: off - * 1-7: min - * 8-127: linear from min to max - */ -void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max) +int fb_device_create(struct fb_info *fb_info) { - unsigned int i, flat, count, range = (max - min); - - mutex_lock(&fb_info->bl_curve_mutex); + int node = fb_info->node; + dev_t devt = MKDEV(FB_MAJOR, node); + int ret; + + fb_info->dev = device_create(fb_class, fb_info->device, devt, NULL, "fb%d", node); + if (IS_ERR(fb_info->dev)) { + /* Not fatal */ + ret = PTR_ERR(fb_info->dev); + pr_warn("Unable to create device for framebuffer %d; error %d\n", node, ret); + fb_info->dev = NULL; + } else { + fb_init_device(fb_info); + } - fb_info->bl_curve[0] = off; + return 0; +} - for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat) - fb_info->bl_curve[flat] = min; +void fb_device_destroy(struct fb_info *fb_info) +{ + dev_t devt = MKDEV(FB_MAJOR, fb_info->node); - count = FB_BACKLIGHT_LEVELS * 15 / 16; - for (i = 0; i < count; ++i) - fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count); + if (!fb_info->dev) + return; - mutex_unlock(&fb_info->bl_curve_mutex); + fb_cleanup_device(fb_info); + device_destroy(fb_class, devt); + fb_info->dev = NULL; } -EXPORT_SYMBOL_GPL(fb_bl_default_curve); -#endif diff --git a/drivers/video/fbdev/ep93xx-fb.c b/drivers/video/fbdev/ep93xx-fb.c index 94fe52928be2..37309f9dbe82 100644 --- a/drivers/video/fbdev/ep93xx-fb.c +++ b/drivers/video/fbdev/ep93xx-fb.c @@ -312,7 +312,7 @@ static int ep93xxfb_mmap(struct fb_info *info, struct vm_area_struct *vma) unsigned int offset = vma->vm_pgoff << PAGE_SHIFT; if (offset < info->fix.smem_len) { - return dma_mmap_wc(info->dev, vma, info->screen_base, + return dma_mmap_wc(info->device, vma, info->screen_base, info->fix.smem_start, info->fix.smem_len); } @@ -423,7 +423,7 @@ static int ep93xxfb_alloc_videomem(struct fb_info *info) /* Maximum 16bpp -> used memory is maximum x*y*2 bytes */ fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * 2; - virt_addr = dma_alloc_wc(info->dev, fb_size, &phys_addr, GFP_KERNEL); + virt_addr = dma_alloc_wc(info->device, fb_size, &phys_addr, GFP_KERNEL); if (!virt_addr) return -ENOMEM; @@ -436,11 +436,11 @@ static int ep93xxfb_alloc_videomem(struct fb_info *info) * least. */ if (check_screenpage_bug && phys_addr & (1 << 27)) { - dev_err(info->dev, "ep93xx framebuffer bug. phys addr (0x%x) " - "has bit 27 set: cannot init framebuffer\n", - phys_addr); + fb_err(info, "ep93xx framebuffer bug. phys addr (0x%x) " + "has bit 27 set: cannot init framebuffer\n", + phys_addr); - dma_free_coherent(info->dev, fb_size, virt_addr, phys_addr); + dma_free_coherent(info->device, fb_size, virt_addr, phys_addr); return -ENOMEM; } @@ -454,7 +454,7 @@ static int ep93xxfb_alloc_videomem(struct fb_info *info) static void ep93xxfb_dealloc_videomem(struct fb_info *info) { if (info->screen_base) - dma_free_coherent(info->dev, info->fix.smem_len, + dma_free_coherent(info->device, info->fix.smem_len, info->screen_base, info->fix.smem_start); } @@ -474,7 +474,6 @@ static int ep93xxfb_probe(struct platform_device *pdev) if (!info) return -ENOMEM; - info->dev = &pdev->dev; platform_set_drvdata(pdev, info); fbi = info->par; fbi->mach_info = mach_info; @@ -525,7 +524,7 @@ static int ep93xxfb_probe(struct platform_device *pdev) err = fb_find_mode(&info->var, info, video_mode, NULL, 0, NULL, 16); if (err == 0) { - dev_err(info->dev, "No suitable video mode found\n"); + fb_err(info, "No suitable video mode found\n"); err = -EINVAL; goto failed_resource; } @@ -554,8 +553,8 @@ static int ep93xxfb_probe(struct platform_device *pdev) if (err) goto failed_framebuffer; - dev_info(info->dev, "registered. Mode = %dx%d-%d\n", - info->var.xres, info->var.yres, info->var.bits_per_pixel); + fb_info(info, "registered. Mode = %dx%d-%d\n", + info->var.xres, info->var.yres, info->var.bits_per_pixel); return 0; failed_framebuffer: diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c index 730a07d23fa9..785eb8a06943 100644 --- a/drivers/video/fbdev/fsl-diu-fb.c +++ b/drivers/video/fbdev/fsl-diu-fb.c @@ -872,7 +872,7 @@ static int map_video_memory(struct fb_info *info) p = alloc_pages_exact(smem_len, GFP_DMA | __GFP_ZERO); if (!p) { - dev_err(info->dev, "unable to allocate fb memory\n"); + fb_err(info, "unable to allocate fb memory\n"); return -ENOMEM; } mutex_lock(&info->mm_lock); @@ -1145,7 +1145,7 @@ static int fsl_diu_set_par(struct fb_info *info) /* Memory allocation for framebuffer */ if (map_video_memory(info)) { - dev_err(info->dev, "unable to allocate fb memory 1\n"); + fb_err(info, "unable to allocate fb memory 1\n"); return -ENOMEM; } } @@ -1277,16 +1277,16 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, if (!arg) return -EINVAL; - dev_dbg(info->dev, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd, + fb_dbg(info, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd, _IOC_DIR(cmd) & _IOC_READ ? "R" : "", _IOC_DIR(cmd) & _IOC_WRITE ? "W" : "", _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd)); switch (cmd) { case MFB_SET_PIXFMT_OLD: - dev_warn(info->dev, - "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n", - MFB_SET_PIXFMT_OLD); + fb_warn(info, + "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n", + MFB_SET_PIXFMT_OLD); fallthrough; case MFB_SET_PIXFMT: if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt))) @@ -1294,9 +1294,9 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, ad->pix_fmt = pix_fmt; break; case MFB_GET_PIXFMT_OLD: - dev_warn(info->dev, - "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n", - MFB_GET_PIXFMT_OLD); + fb_warn(info, + "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n", + MFB_GET_PIXFMT_OLD); fallthrough; case MFB_GET_PIXFMT: pix_fmt = ad->pix_fmt; @@ -1375,7 +1375,7 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, } #endif default: - dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd); + fb_err(info, "unknown ioctl command (0x%08X)\n", cmd); return -ENOIOCTLCMD; } @@ -1543,21 +1543,21 @@ static int install_fb(struct fb_info *info) } if (fsl_diu_check_var(&info->var, info)) { - dev_err(info->dev, "fsl_diu_check_var failed\n"); + fb_err(info, "fsl_diu_check_var failed\n"); unmap_video_memory(info); fb_dealloc_cmap(&info->cmap); return -EINVAL; } if (register_framebuffer(info) < 0) { - dev_err(info->dev, "register_framebuffer failed\n"); + fb_err(info, "register_framebuffer failed\n"); unmap_video_memory(info); fb_dealloc_cmap(&info->cmap); return -EINVAL; } mfbi->registered = 1; - dev_info(info->dev, "%s registered successfully\n", mfbi->id); + fb_info(info, "%s registered successfully\n", mfbi->id); return 0; } diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c index 7ce0a16ce8b9..5043d08ade54 100644 --- a/drivers/video/fbdev/hecubafb.c +++ b/drivers/video/fbdev/hecubafb.c @@ -120,90 +120,28 @@ static void hecubafb_dpy_deferred_io(struct fb_info *info, struct list_head *pag hecubafb_dpy_update(info->par); } -static void hecubafb_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) +static void hecubafb_defio_damage_range(struct fb_info *info, off_t off, size_t len) { struct hecubafb_par *par = info->par; - sys_fillrect(info, rect); - hecubafb_dpy_update(par); } -static void hecubafb_copyarea(struct fb_info *info, - const struct fb_copyarea *area) +static void hecubafb_defio_damage_area(struct fb_info *info, u32 x, u32 y, + u32 width, u32 height) { struct hecubafb_par *par = info->par; - sys_copyarea(info, area); - hecubafb_dpy_update(par); } -static void hecubafb_imageblit(struct fb_info *info, - const struct fb_image *image) -{ - struct hecubafb_par *par = info->par; - - sys_imageblit(info, image); - - hecubafb_dpy_update(par); -} - -/* - * this is the slow path from userspace. they can seek and write to - * the fb. it's inefficient to do anything less than a full screen draw - */ -static ssize_t hecubafb_write(struct fb_info *info, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hecubafb_par *par = info->par; - unsigned long p = *ppos; - void *dst; - int err = 0; - unsigned long total_size; - - if (!info->screen_buffer) - return -ENODEV; - - total_size = info->fix.smem_len; - - if (p > total_size) - return -EFBIG; - - if (count > total_size) { - err = -EFBIG; - count = total_size; - } - - if (count + p > total_size) { - if (!err) - err = -ENOSPC; - - count = total_size - p; - } - - dst = info->screen_buffer + p; - - if (copy_from_user(dst, buf, count)) - err = -EFAULT; - - if (!err) - *ppos += count; - - hecubafb_dpy_update(par); - - return (err) ? err : count; -} +FB_GEN_DEFAULT_DEFERRED_SYS_OPS(hecubafb, + hecubafb_defio_damage_range, + hecubafb_defio_damage_area) static const struct fb_ops hecubafb_ops = { - .owner = THIS_MODULE, - .fb_read = fb_sys_read, - .fb_write = hecubafb_write, - .fb_fillrect = hecubafb_fillrect, - .fb_copyarea = hecubafb_copyarea, - .fb_imageblit = hecubafb_imageblit, - .fb_mmap = fb_deferred_io_mmap, + .owner = THIS_MODULE, + FB_DEFAULT_DEFERRED_OPS(hecubafb), }; static struct fb_deferred_io hecubafb_defio = { diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index 1ae35ab62b29..b331452aab4f 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -48,6 +48,7 @@ #include <linux/aperture.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/screen_info.h> #include <linux/vmalloc.h> #include <linux/init.h> #include <linux/completion.h> diff --git a/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c b/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c index b5c8fcab9940..119c2a582ecb 100644 --- a/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c +++ b/drivers/video/fbdev/mb862xx/mb862xxfbdrv.c @@ -112,8 +112,7 @@ static int mb862xxfb_check_var(struct fb_var_screeninfo *var, { unsigned long tmp; - if (fbi->dev) - dev_dbg(fbi->dev, "%s\n", __func__); + fb_dbg(fbi, "%s\n", __func__); /* check if these values fit into the registers */ if (var->hsync_len > 255 || var->vsync_len > 255) @@ -290,7 +289,7 @@ static int mb862xxfb_blank(int mode, struct fb_info *fbi) struct mb862xxfb_par *par = fbi->par; unsigned long reg; - dev_dbg(fbi->dev, "blank mode=%d\n", mode); + fb_dbg(fbi, "blank mode=%d\n", mode); switch (mode) { case FB_BLANK_POWERDOWN: @@ -791,7 +790,7 @@ static void of_platform_mb862xx_remove(struct platform_device *ofdev) resource_size_t res_size = resource_size(par->res); unsigned long reg; - dev_dbg(fbi->dev, "%s release\n", fbi->fix.id); + fb_dbg(fbi, "%s release\n", fbi->fix.id); /* display off */ reg = inreg(disp, GC_DCM1); @@ -1138,7 +1137,7 @@ static void mb862xx_pci_remove(struct pci_dev *pdev) struct mb862xxfb_par *par = fbi->par; unsigned long reg; - dev_dbg(fbi->dev, "%s release\n", fbi->fix.id); + fb_dbg(fbi, "%s release\n", fbi->fix.id); /* display off */ reg = inreg(disp, GC_DCM1); diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c index ebdb4949c4ac..12565eeb502c 100644 --- a/drivers/video/fbdev/metronomefb.c +++ b/drivers/video/fbdev/metronomefb.c @@ -181,7 +181,7 @@ static int load_waveform(u8 *mem, size_t size, int m, int t, int mem_idx = 0; struct waveform_hdr *wfm_hdr; u8 *metromem = par->metromem_wfm; - struct device *dev = par->info->dev; + struct device *dev = par->info->device; if (user_wfm_size) epd_frame_table[par->dt].wfm_size = user_wfm_size; @@ -483,86 +483,28 @@ static void metronomefb_dpy_deferred_io(struct fb_info *info, struct list_head * metronome_display_cmd(par); } -static void metronomefb_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) +static void metronomefb_defio_damage_range(struct fb_info *info, off_t off, size_t len) { struct metronomefb_par *par = info->par; - sys_fillrect(info, rect); metronomefb_dpy_update(par); } -static void metronomefb_copyarea(struct fb_info *info, - const struct fb_copyarea *area) +static void metronomefb_defio_damage_area(struct fb_info *info, u32 x, u32 y, + u32 width, u32 height) { struct metronomefb_par *par = info->par; - sys_copyarea(info, area); metronomefb_dpy_update(par); } -static void metronomefb_imageblit(struct fb_info *info, - const struct fb_image *image) -{ - struct metronomefb_par *par = info->par; - - sys_imageblit(info, image); - metronomefb_dpy_update(par); -} - -/* - * this is the slow path from userspace. they can seek and write to - * the fb. it is based on fb_sys_write - */ -static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct metronomefb_par *par = info->par; - unsigned long p = *ppos; - void *dst; - int err = 0; - unsigned long total_size; - - if (!info->screen_buffer) - return -ENODEV; - - total_size = info->fix.smem_len; - - if (p > total_size) - return -EFBIG; - - if (count > total_size) { - err = -EFBIG; - count = total_size; - } - - if (count + p > total_size) { - if (!err) - err = -ENOSPC; - - count = total_size - p; - } - - dst = info->screen_buffer + p; - - if (copy_from_user(dst, buf, count)) - err = -EFAULT; - - if (!err) - *ppos += count; - - metronomefb_dpy_update(par); - - return (err) ? err : count; -} +FB_GEN_DEFAULT_DEFERRED_SYS_OPS(metronomefb, + metronomefb_defio_damage_range, + metronomefb_defio_damage_area) static const struct fb_ops metronomefb_ops = { - .owner = THIS_MODULE, - .fb_write = metronomefb_write, - .fb_fillrect = metronomefb_fillrect, - .fb_copyarea = metronomefb_copyarea, - .fb_imageblit = metronomefb_imageblit, - .fb_mmap = fb_deferred_io_mmap, + .owner = THIS_MODULE, + FB_DEFAULT_DEFERRED_OPS(metronomefb), }; static struct fb_deferred_io metronomefb_defio = { diff --git a/drivers/video/fbdev/nvidia/nv_backlight.c b/drivers/video/fbdev/nvidia/nv_backlight.c index 503a7a683855..160da9c50a52 100644 --- a/drivers/video/fbdev/nvidia/nv_backlight.c +++ b/drivers/video/fbdev/nvidia/nv_backlight.c @@ -98,7 +98,7 @@ void nvidia_bl_init(struct nvidia_par *par) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = FB_BACKLIGHT_LEVELS - 1; - bd = backlight_device_register(name, info->dev, par, &nvidia_bl_ops, + bd = backlight_device_register(name, info->device, par, &nvidia_bl_ops, &props); if (IS_ERR(bd)) { info->bl_dev = NULL; diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c index ea4ba3dfb96b..039e886346fa 100644 --- a/drivers/video/fbdev/nvidia/nvidia.c +++ b/drivers/video/fbdev/nvidia/nvidia.c @@ -1400,14 +1400,14 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) pci_set_drvdata(pd, info); - if (backlight) - nvidia_bl_init(par); - if (register_framebuffer(info) < 0) { printk(KERN_ERR PFX "error registering nVidia framebuffer\n"); goto err_out_iounmap_fb; } + if (backlight) + nvidia_bl_init(par); + printk(KERN_INFO PFX "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n", info->fix.id, @@ -1439,9 +1439,9 @@ static void nvidiafb_remove(struct pci_dev *pd) NVTRACE_ENTER(); + nvidia_bl_exit(par); unregister_framebuffer(info); - nvidia_bl_exit(par); arch_phys_wc_del(par->wc_cookie); iounmap(info->screen_base); fb_destroy_modedb(info->monspecs.modedb); diff --git a/drivers/video/fbdev/omap2/omapfb/Kconfig b/drivers/video/fbdev/omap2/omapfb/Kconfig index 69f9cb03507e..21069fdb7cc2 100644 --- a/drivers/video/fbdev/omap2/omapfb/Kconfig +++ b/drivers/video/fbdev/omap2/omapfb/Kconfig @@ -5,9 +5,9 @@ config OMAP2_VRFB menuconfig FB_OMAP2 tristate "OMAP2+ frame buffer support" depends on FB + depends on FB_DEVICE depends on DRM_OMAP = n depends on GPIOLIB - select FB_OMAP2_DSS select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select FB_CFB_FILLRECT diff --git a/drivers/video/fbdev/pxa168fb.c b/drivers/video/fbdev/pxa168fb.c index 79f338463092..82cb9ffe5290 100644 --- a/drivers/video/fbdev/pxa168fb.c +++ b/drivers/video/fbdev/pxa168fb.c @@ -629,7 +629,7 @@ static int pxa168fb_probe(struct platform_device *pdev) fbi = info->par; fbi->info = info; fbi->clk = clk; - fbi->dev = info->dev = &pdev->dev; + fbi->dev = &pdev->dev; fbi->panel_rbswap = mi->panel_rbswap; fbi->is_blanked = 0; fbi->active = mi->active; diff --git a/drivers/video/fbdev/riva/fbdev.c b/drivers/video/fbdev/riva/fbdev.c index 41edc6e79460..6ade8de5df4a 100644 --- a/drivers/video/fbdev/riva/fbdev.c +++ b/drivers/video/fbdev/riva/fbdev.c @@ -333,7 +333,7 @@ static void riva_bl_init(struct riva_par *par) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = FB_BACKLIGHT_LEVELS - 1; - bd = backlight_device_register(name, info->dev, par, &riva_bl_ops, + bd = backlight_device_register(name, info->device, par, &riva_bl_ops, &props); if (IS_ERR(bd)) { info->bl_dev = NULL; @@ -2031,9 +2031,6 @@ static int rivafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) pci_set_drvdata(pd, info); - if (backlight) - riva_bl_init(info->par); - ret = register_framebuffer(info); if (ret < 0) { printk(KERN_ERR PFX @@ -2041,6 +2038,9 @@ static int rivafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) goto err_iounmap_screen_base; } + if (backlight) + riva_bl_init(info->par); + printk(KERN_INFO PFX "PCI nVidia %s framebuffer ver %s (%dMB @ 0x%lX)\n", info->fix.id, @@ -2084,9 +2084,9 @@ static void rivafb_remove(struct pci_dev *pd) kfree(par->EDID); #endif + riva_bl_exit(info); unregister_framebuffer(info); - riva_bl_exit(info); arch_phys_wc_del(par->wc_cookie); iounmap(par->ctrl_base); iounmap(info->screen_base); diff --git a/drivers/video/fbdev/sh7760fb.c b/drivers/video/fbdev/sh7760fb.c index 98c5227098a8..db898112f9a5 100644 --- a/drivers/video/fbdev/sh7760fb.c +++ b/drivers/video/fbdev/sh7760fb.c @@ -118,7 +118,7 @@ static int sh7760_setcolreg (u_int regno, return 0; } -static int sh7760fb_get_color_info(struct device *dev, +static int sh7760fb_get_color_info(struct fb_info *info, u16 lddfr, int *bpp, int *gray) { int lbpp, lgray; @@ -152,7 +152,7 @@ static int sh7760fb_get_color_info(struct device *dev, lgray = 0; break; default: - dev_dbg(dev, "unsupported LDDFR bit depth.\n"); + fb_dbg(info, "unsupported LDDFR bit depth.\n"); return -EINVAL; } @@ -172,7 +172,7 @@ static int sh7760fb_check_var(struct fb_var_screeninfo *var, int ret, bpp; /* get color info from register value */ - ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL); + ret = sh7760fb_get_color_info(info, par->pd->lddfr, &bpp, NULL); if (ret) return ret; @@ -209,7 +209,7 @@ static int sh7760fb_set_par(struct fb_info *info) /* rotate only works with xres <= 320 */ if (par->rot && (vm->xres > 320)) { - dev_dbg(info->dev, "rotation disabled due to display size\n"); + fb_dbg(info, "rotation disabled due to display size\n"); par->rot = 0; } @@ -224,11 +224,11 @@ static int sh7760fb_set_par(struct fb_info *info) vdln = vm->yres; /* get color info from register value */ - ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, &gray); + ret = sh7760fb_get_color_info(info, par->pd->lddfr, &bpp, &gray); if (ret) return ret; - dev_dbg(info->dev, "%dx%d %dbpp %s (orientation %s)\n", hdcn, + fb_dbg(info, "%dx%d %dbpp %s (orientation %s)\n", hdcn, vdln, bpp, gray ? "grayscale" : "color", par->rot ? "rotated" : "normal"); @@ -308,7 +308,7 @@ static int sh7760fb_set_par(struct fb_info *info) if (((ldmtr & 0x003f) >= LDMTR_DSTN_MONO_8) && ((ldmtr & 0x003f) <= LDMTR_DSTN_COLOR_16)) { - dev_dbg(info->dev, " ***** DSTN untested! *****\n"); + fb_dbg(info, " ***** DSTN untested! *****\n"); dstn_off = stride; if (par->rot) @@ -328,17 +328,17 @@ static int sh7760fb_set_par(struct fb_info *info) sh7760fb_blank(FB_BLANK_UNBLANK, info); /* panel on! */ - dev_dbg(info->dev, "hdcn : %6d htcn : %6d\n", hdcn, htcn); - dev_dbg(info->dev, "hsynw : %6d hsynp : %6d\n", hsynw, hsynp); - dev_dbg(info->dev, "vdln : %6d vtln : %6d\n", vdln, vtln); - dev_dbg(info->dev, "vsynw : %6d vsynp : %6d\n", vsynw, vsynp); - dev_dbg(info->dev, "clksrc: %6d clkdiv: %6d\n", + fb_dbg(info, "hdcn : %6d htcn : %6d\n", hdcn, htcn); + fb_dbg(info, "hsynw : %6d hsynp : %6d\n", hsynw, hsynp); + fb_dbg(info, "vdln : %6d vtln : %6d\n", vdln, vtln); + fb_dbg(info, "vsynw : %6d vsynp : %6d\n", vsynw, vsynp); + fb_dbg(info, "clksrc: %6d clkdiv: %6d\n", (par->pd->ldickr >> 12) & 3, par->pd->ldickr & 0x1f); - dev_dbg(info->dev, "ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr, + fb_dbg(info, "ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr, par->pd->ldpspr); - dev_dbg(info->dev, "ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr); - dev_dbg(info->dev, "ldlaor: %ld\n", stride); - dev_dbg(info->dev, "ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl); + fb_dbg(info, "ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr); + fb_dbg(info, "ldlaor: %ld\n", stride); + fb_dbg(info, "ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl); return 0; } @@ -361,7 +361,7 @@ static void sh7760fb_free_mem(struct fb_info *info) if (!info->screen_base) return; - dma_free_coherent(info->dev, info->screen_size, + dma_free_coherent(info->device, info->screen_size, info->screen_base, par->fbdma); par->fbdma = 0; @@ -383,7 +383,7 @@ static int sh7760fb_alloc_mem(struct fb_info *info) return 0; /* get color info from register value */ - ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL); + ret = sh7760fb_get_color_info(info, par->pd->lddfr, &bpp, NULL); if (ret) { printk(KERN_ERR "colinfo\n"); return ret; @@ -403,21 +403,21 @@ static int sh7760fb_alloc_mem(struct fb_info *info) } else if (bpp > 8) vram *= 2; if ((vram < 1) || (vram > 1024 * 2048)) { - dev_dbg(info->dev, "too much VRAM required. Check settings\n"); + fb_dbg(info, "too much VRAM required. Check settings\n"); return -ENODEV; } if (vram < PAGE_SIZE) vram = PAGE_SIZE; - fbmem = dma_alloc_coherent(info->dev, vram, &par->fbdma, GFP_KERNEL); + fbmem = dma_alloc_coherent(info->device, vram, &par->fbdma, GFP_KERNEL); if (!fbmem) return -ENOMEM; if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) { sh7760fb_free_mem(info); - dev_err(info->dev, "kernel gave me memory at 0x%08lx, which is" + dev_err(info->device, "kernel gave me memory at 0x%08lx, which is" "unusable for the LCDC\n", (unsigned long)par->fbdma); return -ENOMEM; } @@ -452,7 +452,7 @@ static int sh7760fb_probe(struct platform_device *pdev) par->pd = pdev->dev.platform_data; if (!par->pd) { - dev_dbg(info->dev, "no display setup data!\n"); + dev_dbg(&pdev->dev, "no display setup data!\n"); ret = -ENODEV; goto out_fb; } @@ -488,7 +488,7 @@ static int sh7760fb_probe(struct platform_device *pdev) ret = sh7760fb_alloc_mem(info); if (ret) { - dev_dbg(info->dev, "framebuffer memory allocation failed!\n"); + dev_dbg(info->device, "framebuffer memory allocation failed!\n"); goto out_unmap; } @@ -521,13 +521,13 @@ static int sh7760fb_probe(struct platform_device *pdev) ret = fb_alloc_cmap(&info->cmap, 256, 0); if (ret) { - dev_dbg(info->dev, "Unable to allocate cmap memory\n"); + dev_dbg(&pdev->dev, "Unable to allocate cmap memory\n"); goto out_mem; } ret = register_framebuffer(info); if (ret < 0) { - dev_dbg(info->dev, "cannot register fb!\n"); + dev_dbg(&pdev->dev, "cannot register fb!\n"); goto out_cmap; } platform_set_drvdata(pdev, info); diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c index e0d29be1565b..46951a095274 100644 --- a/drivers/video/fbdev/sm501fb.c +++ b/drivers/video/fbdev/sm501fb.c @@ -1293,7 +1293,7 @@ static int sm501fb_sync(struct fb_info *info) count--; if (count <= 0) { - dev_err(info->dev, "Timeout waiting for 2d engine sync\n"); + fb_err(info, "Timeout waiting for 2d engine sync\n"); return 1; } return 0; diff --git a/drivers/video/fbdev/sm712fb.c b/drivers/video/fbdev/sm712fb.c index b7ad3c644e13..f929091da4e7 100644 --- a/drivers/video/fbdev/sm712fb.c +++ b/drivers/video/fbdev/sm712fb.c @@ -27,12 +27,17 @@ #include <linux/uaccess.h> #include <linux/module.h> #include <linux/console.h> -#include <linux/screen_info.h> #include <linux/pm.h> #include "sm712.h" +struct smtcfb_screen_info { + u16 lfb_width; + u16 lfb_height; + u16 lfb_depth; +}; + /* * Private structure */ @@ -829,7 +834,7 @@ static const struct modeinit vgamode[] = { }, }; -static struct screen_info smtc_scr_info; +static struct smtcfb_screen_info smtc_scr_info; static char *mode_option; diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c index 17cec62cc65d..adb2b1fe8383 100644 --- a/drivers/video/fbdev/smscufx.c +++ b/drivers/video/fbdev/smscufx.c @@ -1496,7 +1496,7 @@ static int ufx_setup_modes(struct ufx_data *dev, struct fb_info *info, u8 *edid; int i, result = 0, tries = 3; - if (info->dev) /* only use mutex if info has been registered */ + if (refcount_read(&info->count)) /* only use mutex if info has been registered */ mutex_lock(&info->lock); edid = kmalloc(EDID_LENGTH, GFP_KERNEL); @@ -1610,7 +1610,7 @@ error: if (edid && (dev->edid != edid)) kfree(edid); - if (info->dev) + if (refcount_read(&info->count)) mutex_unlock(&info->lock); return result; diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 11c373798279..a2b939342a4f 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -292,43 +292,6 @@ static int ssd1307fb_update_display(struct ssd1307fb_par *par) return ssd1307fb_update_rect(par, 0, 0, par->width, par->height); } -static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct ssd1307fb_par *par = info->par; - unsigned long total_size; - unsigned long p = *ppos; - void *dst; - int ret; - - if (!info->screen_buffer) - return -ENODEV; - - total_size = info->fix.smem_len; - - if (p > total_size) - return -EINVAL; - - if (count + p > total_size) - count = total_size - p; - - if (!count) - return -EINVAL; - - dst = info->screen_buffer + p; - - if (copy_from_user(dst, buf, count)) - return -EFAULT; - - ret = ssd1307fb_update_display(par); - if (ret < 0) - return ret; - - *ppos += count; - - return count; -} - static int ssd1307fb_blank(int blank_mode, struct fb_info *info) { struct ssd1307fb_par *par = info->par; @@ -339,39 +302,29 @@ static int ssd1307fb_blank(int blank_mode, struct fb_info *info) return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); } -static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +static void ssd1307fb_defio_damage_range(struct fb_info *info, off_t off, size_t len) { struct ssd1307fb_par *par = info->par; - sys_fillrect(info, rect); - ssd1307fb_update_rect(par, rect->dx, rect->dy, rect->width, - rect->height); -} -static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) -{ - struct ssd1307fb_par *par = info->par; - sys_copyarea(info, area); - ssd1307fb_update_rect(par, area->dx, area->dy, area->width, - area->height); + ssd1307fb_update_display(par); } -static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image) +static void ssd1307fb_defio_damage_area(struct fb_info *info, u32 x, u32 y, + u32 width, u32 height) { struct ssd1307fb_par *par = info->par; - sys_imageblit(info, image); - ssd1307fb_update_rect(par, image->dx, image->dy, image->width, - image->height); + + ssd1307fb_update_rect(par, x, y, width, height); } +FB_GEN_DEFAULT_DEFERRED_SYS_OPS(ssd1307fb, + ssd1307fb_defio_damage_range, + ssd1307fb_defio_damage_area) + static const struct fb_ops ssd1307fb_ops = { .owner = THIS_MODULE, - .fb_read = fb_sys_read, - .fb_write = ssd1307fb_write, + FB_DEFAULT_DEFERRED_OPS(ssd1307fb), .fb_blank = ssd1307fb_blank, - .fb_fillrect = ssd1307fb_fillrect, - .fb_copyarea = ssd1307fb_copyarea, - .fb_imageblit = ssd1307fb_imageblit, - .fb_mmap = fb_deferred_io_mmap, }; static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist) diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c index cdf8e9fe9948..dd0fa42eceb9 100644 --- a/drivers/video/fbdev/tdfxfb.c +++ b/drivers/video/fbdev/tdfxfb.c @@ -1327,8 +1327,8 @@ static void tdfxfb_create_i2c_busses(struct fb_info *info) par->chan[0].par = par; par->chan[1].par = par; - tdfxfb_setup_ddc_bus(&par->chan[0], "Voodoo3-DDC", info->dev); - tdfxfb_setup_i2c_bus(&par->chan[1], "Voodoo3-I2C", info->dev); + tdfxfb_setup_ddc_bus(&par->chan[0], "Voodoo3-DDC", info->device); + tdfxfb_setup_i2c_bus(&par->chan[1], "Voodoo3-I2C", info->device); } static void tdfxfb_delete_i2c_busses(struct tdfx_par *par) diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c index 9b2a786621a6..6664dc7a5a41 100644 --- a/drivers/video/fbdev/xen-fbfront.c +++ b/drivers/video/fbdev/xen-fbfront.c @@ -240,41 +240,6 @@ static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green, return 0; } -static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) -{ - struct xenfb_info *info = p->par; - - sys_fillrect(p, rect); - xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height); -} - -static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image) -{ - struct xenfb_info *info = p->par; - - sys_imageblit(p, image); - xenfb_refresh(info, image->dx, image->dy, image->width, image->height); -} - -static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) -{ - struct xenfb_info *info = p->par; - - sys_copyarea(p, area); - xenfb_refresh(info, area->dx, area->dy, area->width, area->height); -} - -static ssize_t xenfb_write(struct fb_info *p, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct xenfb_info *info = p->par; - ssize_t res; - - res = fb_sys_write(p, buf, count, ppos); - xenfb_refresh(info, 0, 0, info->page->width, info->page->height); - return res; -} - static int xenfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -326,17 +291,31 @@ static int xenfb_set_par(struct fb_info *info) return 0; } +static void xenfb_defio_damage_range(struct fb_info *info, off_t off, size_t len) +{ + struct xenfb_info *xenfb_info = info->par; + + xenfb_refresh(xenfb_info, 0, 0, xenfb_info->page->width, xenfb_info->page->height); +} + +static void xenfb_defio_damage_area(struct fb_info *info, u32 x, u32 y, + u32 width, u32 height) +{ + struct xenfb_info *xenfb_info = info->par; + + xenfb_refresh(xenfb_info, x, y, width, height); +} + +FB_GEN_DEFAULT_DEFERRED_SYS_OPS(xenfb, + xenfb_defio_damage_range, + xenfb_defio_damage_area) + static const struct fb_ops xenfb_fb_ops = { .owner = THIS_MODULE, - .fb_read = fb_sys_read, - .fb_write = xenfb_write, + FB_DEFAULT_DEFERRED_OPS(xenfb), .fb_setcolreg = xenfb_setcolreg, - .fb_fillrect = xenfb_fillrect, - .fb_copyarea = xenfb_copyarea, - .fb_imageblit = xenfb_imageblit, .fb_check_var = xenfb_check_var, .fb_set_par = xenfb_set_par, - .fb_mmap = fb_deferred_io_mmap, }; static irqreturn_t xenfb_event_handler(int rq, void *dev_id) diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index f668e75fbabe..6a46baa0737c 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -206,4 +206,6 @@ void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, bool force, bool disabled, bool rxsense); void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data); +bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi); + #endif /* __IMX_HDMI_H__ */ diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 89e2706cac56..b77f2c7275b7 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -304,22 +304,14 @@ struct drm_driver { /** * @prime_handle_to_fd: * - * Main PRIME export function. Should be implemented with - * drm_gem_prime_handle_to_fd() for GEM based drivers. - * - * For an in-depth discussion see :ref:`PRIME buffer sharing - * documentation <prime_buffer_sharing>`. + * PRIME export function. Only used by vmwgfx. */ int (*prime_handle_to_fd)(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd); /** * @prime_fd_to_handle: * - * Main PRIME import function. Should be implemented with - * drm_gem_prime_fd_to_handle() for GEM based drivers. - * - * For an in-depth discussion see :ref:`PRIME buffer sharing - * documentation <prime_buffer_sharing>`. + * PRIME import function. Only used by vmwgfx. */ int (*prime_fd_to_handle)(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle); @@ -343,20 +335,6 @@ struct drm_driver { struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sgt); - /** - * @gem_prime_mmap: - * - * mmap hook for GEM drivers, used to implement dma-buf mmap in the - * PRIME helpers. - * - * This hook only exists for historical reasons. Drivers must use - * drm_gem_prime_mmap() to implement it. - * - * FIXME: Convert all drivers to implement mmap in struct - * &drm_gem_object_funcs and inline drm_gem_prime_mmap() into - * its callers. This hook should be removed afterwards. - */ - int (*gem_prime_mmap)(struct drm_gem_object *obj, struct vm_area_struct *vma); /** * @dumb_create: diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h new file mode 100644 index 000000000000..73205afec162 --- /dev/null +++ b/include/drm/drm_exec.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +#ifndef __DRM_EXEC_H__ +#define __DRM_EXEC_H__ + +#include <linux/ww_mutex.h> + +#define DRM_EXEC_INTERRUPTIBLE_WAIT BIT(0) +#define DRM_EXEC_IGNORE_DUPLICATES BIT(1) + +struct drm_gem_object; + +/** + * struct drm_exec - Execution context + */ +struct drm_exec { + /** + * @flags: Flags to control locking behavior + */ + uint32_t flags; + + /** + * @ticket: WW ticket used for acquiring locks + */ + struct ww_acquire_ctx ticket; + + /** + * @num_objects: number of objects locked + */ + unsigned int num_objects; + + /** + * @max_objects: maximum objects in array + */ + unsigned int max_objects; + + /** + * @objects: array of the locked objects + */ + struct drm_gem_object **objects; + + /** + * @contended: contended GEM object we backed off for + */ + struct drm_gem_object *contended; + + /** + * @prelocked: already locked GEM object due to contention + */ + struct drm_gem_object *prelocked; +}; + +/** + * drm_exec_for_each_locked_object - iterate over all the locked objects + * @exec: drm_exec object + * @index: unsigned long index for the iteration + * @obj: the current GEM object + * + * Iterate over all the locked GEM objects inside the drm_exec object. + */ +#define drm_exec_for_each_locked_object(exec, index, obj) \ + for (index = 0, obj = (exec)->objects[0]; \ + index < (exec)->num_objects; \ + ++index, obj = (exec)->objects[index]) + +/** + * drm_exec_until_all_locked - loop until all GEM objects are locked + * @exec: drm_exec object + * + * Core functionality of the drm_exec object. Loops until all GEM objects are + * locked and no more contention exists. At the beginning of the loop it is + * guaranteed that no GEM object is locked. + * + * Since labels can't be defined local to the loops body we use a jump pointer + * to make sure that the retry is only used from within the loops body. + */ +#define drm_exec_until_all_locked(exec) \ + for (void *__drm_exec_retry_ptr; ({ \ + __label__ __drm_exec_retry; \ +__drm_exec_retry: \ + __drm_exec_retry_ptr = &&__drm_exec_retry; \ + (void)__drm_exec_retry_ptr; \ + drm_exec_cleanup(exec); \ + });) + +/** + * drm_exec_retry_on_contention - restart the loop to grap all locks + * @exec: drm_exec object + * + * Control flow helper to continue when a contention was detected and we need to + * clean up and re-start the loop to prepare all GEM objects. + */ +#define drm_exec_retry_on_contention(exec) \ + do { \ + if (unlikely(drm_exec_is_contended(exec))) \ + goto *__drm_exec_retry_ptr; \ + } while (0) + +/** + * drm_exec_is_contended - check for contention + * @exec: drm_exec object + * + * Returns true if the drm_exec object has run into some contention while + * locking a GEM object and needs to clean up. + */ +static inline bool drm_exec_is_contended(struct drm_exec *exec) +{ + return !!exec->contended; +} + +void drm_exec_init(struct drm_exec *exec, uint32_t flags); +void drm_exec_fini(struct drm_exec *exec); +bool drm_exec_cleanup(struct drm_exec *exec); +int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj); +void drm_exec_unlock_obj(struct drm_exec *exec, struct drm_gem_object *obj); +int drm_exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj, + unsigned int num_fences); +int drm_exec_prepare_array(struct drm_exec *exec, + struct drm_gem_object **objects, + unsigned int num_objects, + unsigned int num_fences); + +#endif diff --git a/include/drm/drm_gem_dma_helper.h b/include/drm/drm_gem_dma_helper.h index 8a043235dad8..a827bde494f6 100644 --- a/include/drm/drm_gem_dma_helper.h +++ b/include/drm/drm_gem_dma_helper.h @@ -166,11 +166,8 @@ drm_gem_dma_prime_import_sg_table(struct drm_device *dev, * DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE() instead. */ #define DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(dumb_create_func) \ - .dumb_create = (dumb_create_func), \ - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \ - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \ - .gem_prime_import_sg_table = drm_gem_dma_prime_import_sg_table, \ - .gem_prime_mmap = drm_gem_prime_mmap + .dumb_create = (dumb_create_func), \ + .gem_prime_import_sg_table = drm_gem_dma_prime_import_sg_table /** * DRM_GEM_DMA_DRIVER_OPS - DMA GEM driver operations @@ -204,11 +201,8 @@ drm_gem_dma_prime_import_sg_table(struct drm_device *dev, * DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE() instead. */ #define DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(dumb_create_func) \ - .dumb_create = dumb_create_func, \ - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \ - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \ - .gem_prime_import_sg_table = drm_gem_dma_prime_import_sg_table_vmap, \ - .gem_prime_mmap = drm_gem_prime_mmap + .dumb_create = (dumb_create_func), \ + .gem_prime_import_sg_table = drm_gem_dma_prime_import_sg_table_vmap /** * DRM_GEM_DMA_DRIVER_OPS_VMAP - DMA GEM driver operations ensuring a virtual diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h index 5994fed5e327..bf0c31aa8fbe 100644 --- a/include/drm/drm_gem_shmem_helper.h +++ b/include/drm/drm_gem_shmem_helper.h @@ -27,11 +27,6 @@ struct drm_gem_shmem_object { struct drm_gem_object base; /** - * @pages_lock: Protects the page table and use count - */ - struct mutex pages_lock; - - /** * @pages: Page table */ struct page **pages; @@ -66,11 +61,6 @@ struct drm_gem_shmem_object { struct sg_table *sgt; /** - * @vmap_lock: Protects the vmap address and use count - */ - struct mutex vmap_lock; - - /** * @vaddr: Kernel virtual address of the backing memory */ void *vaddr; @@ -109,7 +99,6 @@ struct drm_gem_shmem_object { struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size); void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem); -int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem); void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem); int drm_gem_shmem_pin(struct drm_gem_shmem_object *shmem); void drm_gem_shmem_unpin(struct drm_gem_shmem_object *shmem); @@ -128,8 +117,7 @@ static inline bool drm_gem_shmem_is_purgeable(struct drm_gem_shmem_object *shmem !shmem->base.dma_buf && !shmem->base.import_attach; } -void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem); -bool drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem); +void drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem); struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_shmem_object *shmem); struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_shmem_object *shmem); @@ -290,10 +278,7 @@ int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev, * the &drm_driver structure. */ #define DRM_GEM_SHMEM_DRIVER_OPS \ - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \ - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \ .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, \ - .gem_prime_mmap = drm_gem_prime_mmap, \ - .dumb_create = drm_gem_shmem_dumb_create + .dumb_create = drm_gem_shmem_dumb_create #endif /* __DRM_GEM_SHMEM_HELPER_H__ */ diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index f4aab64411d8..e18429f09e53 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -157,12 +157,9 @@ void drm_gem_vram_simple_display_pipe_cleanup_fb( * &struct drm_driver with default functions. */ #define DRM_GEM_VRAM_DRIVER \ - .debugfs_init = drm_vram_mm_debugfs_init, \ - .dumb_create = drm_gem_vram_driver_dumb_create, \ - .dumb_map_offset = drm_gem_ttm_dumb_map_offset, \ - .gem_prime_mmap = drm_gem_prime_mmap, \ - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \ - .prime_fd_to_handle = drm_gem_prime_fd_to_handle + .debugfs_init = drm_vram_mm_debugfs_init, \ + .dumb_create = drm_gem_vram_driver_dumb_create, \ + .dumb_map_offset = drm_gem_ttm_dumb_map_offset /* * VRAM memory manager diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h index 2a1d01e5b56b..a7abf9f3e697 100644 --- a/include/drm/drm_prime.h +++ b/include/drm/drm_prime.h @@ -60,19 +60,12 @@ enum dma_data_direction; struct drm_device; struct drm_gem_object; -struct drm_file; /* core prime functions */ struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev, struct dma_buf_export_info *exp_info); void drm_gem_dmabuf_release(struct dma_buf *dma_buf); -int drm_gem_prime_fd_to_handle(struct drm_device *dev, - struct drm_file *file_priv, int prime_fd, uint32_t *handle); -int drm_gem_prime_handle_to_fd(struct drm_device *dev, - struct drm_file *file_priv, uint32_t handle, uint32_t flags, - int *prime_fd); - /* helper functions for exporting */ int drm_gem_map_attach(struct dma_buf *dma_buf, struct dma_buf_attachment *attach); diff --git a/include/drm/drm_sysfs.h b/include/drm/drm_sysfs.h index 6273cac44e47..96a5d858404b 100644 --- a/include/drm/drm_sysfs.h +++ b/include/drm/drm_sysfs.h @@ -12,6 +12,6 @@ void drm_class_device_unregister(struct device *dev); void drm_sysfs_hotplug_event(struct drm_device *dev); void drm_sysfs_connector_hotplug_event(struct drm_connector *connector); -void drm_sysfs_connector_status_event(struct drm_connector *connector, - struct drm_property *property); +void drm_sysfs_connector_property_event(struct drm_connector *connector, + struct drm_property *property); #endif diff --git a/include/linux/efi.h b/include/linux/efi.h index ab088c662e88..c31a9895ecb8 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -24,10 +24,11 @@ #include <linux/range.h> #include <linux/reboot.h> #include <linux/uuid.h> -#include <linux/screen_info.h> #include <asm/page.h> +struct screen_info; + #define EFI_SUCCESS 0 #define EFI_LOAD_ERROR ( 1 | (1UL << (BITS_PER_LONG-1))) #define EFI_INVALID_PARAMETER ( 2 | (1UL << (BITS_PER_LONG-1))) diff --git a/include/linux/fb.h b/include/linux/fb.h index ce7d588edc3e..1d5c13f34b09 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -481,7 +481,9 @@ struct fb_info { const struct fb_ops *fbops; struct device *device; /* This is the parent */ +#if defined(CONFIG_FB_DEVICE) struct device *dev; /* This is this fb device */ +#endif int class_flag; /* private sysfs flags */ #ifdef CONFIG_FB_TILEBLITTING struct fb_tile_ops *tileops; /* Tile Blitting */ @@ -609,7 +611,6 @@ extern int fb_new_modelist(struct fb_info *info); extern bool fb_center_logo; extern int fb_logo_count; -extern struct class *fb_class; static inline void lock_fb_info(struct fb_info *info) { @@ -735,11 +736,8 @@ static inline bool fb_be_math(struct fb_info *info) #endif /* CONFIG_FB_FOREIGN_ENDIAN */ } -/* drivers/video/fbsysfs.c */ extern struct fb_info *framebuffer_alloc(size_t size, struct device *dev); extern void framebuffer_release(struct fb_info *info); -extern int fb_init_device(struct fb_info *fb_info); -extern void fb_cleanup_device(struct fb_info *head); extern void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max); /* drivers/video/fbmon.c */ diff --git a/include/linux/platform_data/bd6107.h b/include/linux/platform_data/bd6107.h index 54a06a4d2618..596ca4f95cfa 100644 --- a/include/linux/platform_data/bd6107.h +++ b/include/linux/platform_data/bd6107.h @@ -8,7 +8,7 @@ struct device; struct bd6107_platform_data { - struct device *fbdev; + struct device *dev; unsigned int def_value; }; diff --git a/include/linux/platform_data/gpio_backlight.h b/include/linux/platform_data/gpio_backlight.h index 1a8b5b1946fe..323fbf5f7613 100644 --- a/include/linux/platform_data/gpio_backlight.h +++ b/include/linux/platform_data/gpio_backlight.h @@ -8,7 +8,7 @@ struct device; struct gpio_backlight_platform_data { - struct device *fbdev; + struct device *dev; }; #endif diff --git a/include/linux/platform_data/lv5207lp.h b/include/linux/platform_data/lv5207lp.h index c9da8d402750..95d85c1394bc 100644 --- a/include/linux/platform_data/lv5207lp.h +++ b/include/linux/platform_data/lv5207lp.h @@ -8,7 +8,7 @@ struct device; struct lv5207lp_platform_data { - struct device *fbdev; + struct device *dev; unsigned int max_value; unsigned int def_value; }; diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h index c1ef5fc60a3c..19cb803dd5ec 100644 --- a/include/linux/sysfb.h +++ b/include/linux/sysfb.h @@ -9,7 +9,8 @@ #include <linux/kernel.h> #include <linux/platform_data/simplefb.h> -#include <linux/screen_info.h> + +struct screen_info; enum { M_I17, /* 17-Inch iMac */ diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 43691058d28f..92d96a2b6763 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -883,7 +883,7 @@ struct hdr_metadata_infoframe { */ struct { __u16 x, y; - } display_primaries[3]; + } display_primaries[3]; /** * @white_point: White Point of Colorspace Data. * These are coded as unsigned 16-bit values in units of @@ -894,7 +894,7 @@ struct hdr_metadata_infoframe { */ struct { __u16 x, y; - } white_point; + } white_point; /** * @max_display_mastering_luminance: Max Mastering Display Luminance. * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, |