diff options
103 files changed, 3786 insertions, 1340 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt new file mode 100644 index 000000000000..fd39ad34c383 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt @@ -0,0 +1,64 @@ +Parallel to LVDS Encoder +------------------------ + +This binding supports the parallel to LVDS encoders that don't require any +configuration. + +LVDS is a physical layer specification defined in ANSI/TIA/EIA-644-A. Multiple +incompatible data link layers have been used over time to transmit image data +to LVDS panels. This binding targets devices compatible with the following +specifications only. + +[JEIDA] "Digital Interface Standards for Monitor", JEIDA-59-1999, February +1999 (Version 1.0), Japan Electronic Industry Development Association (JEIDA) +[LDI] "Open LVDS Display Interface", May 1999 (Version 0.95), National +Semiconductor +[VESA] "VESA Notebook Panel Standard", October 2007 (Version 1.0), Video +Electronics Standards Association (VESA) + +Those devices have been marketed under the FPD-Link and FlatLink brand names +among others. + + +Required properties: + +- compatible: Must be "lvds-encoder" + +Required nodes: + +This device has two video ports. Their connections are modeled using the OF +graph bindings specified in Documentation/devicetree/bindings/graph.txt. + +- Video port 0 for parallel input +- Video port 1 for LVDS output + + +Example +------- + +lvds-encoder { + compatible = "lvds-encoder"; + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + lvds_enc_in: endpoint { + remote-endpoint = <&display_out_rgb>; + }; + }; + + port@1 { + reg = <1>; + + lvds_enc_out: endpoint { + remote-endpoint = <&lvds_panel_in>; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/display/bridge/megachips-stdpxxxx-ge-b850v3-fw.txt b/Documentation/devicetree/bindings/display/bridge/megachips-stdpxxxx-ge-b850v3-fw.txt new file mode 100644 index 000000000000..7baa6582517e --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/megachips-stdpxxxx-ge-b850v3-fw.txt @@ -0,0 +1,94 @@ +Drivers for the second video output of the GE B850v3: + STDP4028-ge-b850v3-fw bridges (LVDS-DP) + STDP2690-ge-b850v3-fw bridges (DP-DP++) + +The video processing pipeline on the second output on the GE B850v3: + + Host -> LVDS|--(STDP4028)--|DP -> DP|--(STDP2690)--|DP++ -> Video output + +Each bridge has a dedicated flash containing firmware for supporting the custom +design. The result is that, in this design, neither the STDP4028 nor the +STDP2690 behave as the stock bridges would. The compatible strings include the +suffix "-ge-b850v3-fw" to make it clear that the driver is for the bridges with +the firmware specific for the GE B850v3. + +The hardware do not provide control over the video processing pipeline, as the +two bridges behaves as a single one. The only interfaces exposed by the +hardware are EDID, HPD, and interrupts. + +stdp4028-ge-b850v3-fw required properties: + - compatible : "megachips,stdp4028-ge-b850v3-fw" + - reg : I2C bus address + - interrupt-parent : phandle of the interrupt controller that services + interrupts to the device + - interrupts : one interrupt should be described here, as in + <0 IRQ_TYPE_LEVEL_HIGH> + - ports : One input port(reg = <0>) and one output port(reg = <1>) + +stdp2690-ge-b850v3-fw required properties: + compatible : "megachips,stdp2690-ge-b850v3-fw" + - reg : I2C bus address + - ports : One input port(reg = <0>) and one output port(reg = <1>) + +Example: + +&mux2_i2c2 { + status = "okay"; + clock-frequency = <100000>; + + stdp4028@73 { + compatible = "megachips,stdp4028-ge-b850v3-fw"; + #address-cells = <1>; + #size-cells = <0>; + + reg = <0x73>; + + interrupt-parent = <&gpio2>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + stdp4028_in: endpoint { + remote-endpoint = <&lvds0_out>; + }; + }; + port@1 { + reg = <1>; + stdp4028_out: endpoint { + remote-endpoint = <&stdp2690_in>; + }; + }; + }; + }; + + stdp2690@72 { + compatible = "megachips,stdp2690-ge-b850v3-fw"; + #address-cells = <1>; + #size-cells = <0>; + + reg = <0x72>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + stdp2690_in: endpoint { + remote-endpoint = <&stdp4028_out>; + }; + }; + + port@1 { + reg = <1>; + stdp2690_out: endpoint { + /* Connector for external display */ + }; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index 971c3eedb1c7..fa01db7eb66c 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -21,13 +21,19 @@ Freescale i.MX IPUv3 ==================== Required properties: -- compatible: Should be "fsl,<chip>-ipu" +- compatible: Should be "fsl,<chip>-ipu" where <chip> is one of + - imx51 + - imx53 + - imx6q + - imx6qp - reg: should be register base and length as documented in the datasheet - interrupts: Should contain sync interrupt and error interrupt, in this order. - resets: phandle pointing to the system reset controller and reset line index, see reset/fsl,imx-src.txt for details +Additional required properties for fsl,imx6qp-ipu: +- fsl,prg: phandle to prg node associated with this IPU instance Optional properties: - port@[0-3]: Port nodes with endpoint definitions as defined in Documentation/devicetree/bindings/media/video-interfaces.txt. @@ -53,6 +59,57 @@ ipu: ipu@18000000 { }; }; +Freescale i.MX PRE (Prefetch Resolve Engine) +============================================ + +Required properties: +- compatible: should be "fsl,imx6qp-pre" +- reg: should be register base and length as documented in the + datasheet +- clocks : phandle to the PRE axi clock input, as described + in Documentation/devicetree/bindings/clock/clock-bindings.txt and + Documentation/devicetree/bindings/clock/imx6q-clock.txt. +- clock-names: should be "axi" +- interrupts: should contain the PRE interrupt +- fsl,iram: phandle pointing to the mmio-sram device node, that should be + used for the PRE SRAM double buffer. + +example: + +pre@21c8000 { + compatible = "fsl,imx6qp-pre"; + reg = <0x021c8000 0x1000>; + interrupts = <GIC_SPI 90 IRQ_TYPE_EDGE_RISING>; + clocks = <&clks IMX6QDL_CLK_PRE0>; + clock-names = "axi"; + fsl,iram = <&ocram2>; +}; + +Freescale i.MX PRG (Prefetch Resolve Gasket) +============================================ + +Required properties: +- compatible: should be "fsl,imx6qp-prg" +- reg: should be register base and length as documented in the + datasheet +- clocks : phandles to the PRG ipg and axi clock inputs, as described + in Documentation/devicetree/bindings/clock/clock-bindings.txt and + Documentation/devicetree/bindings/clock/imx6q-clock.txt. +- clock-names: should be "ipg" and "axi" +- fsl,pres: phandles to the PRE units attached to this PRG, with the fixed + PRE as the first entry and the muxable PREs following. + +example: + +prg@21cc000 { + compatible = "fsl,imx6qp-prg"; + reg = <0x021cc000 0x1000>; + clocks = <&clks IMX6QDL_CLK_PRG0_APB>, + <&clks IMX6QDL_CLK_PRG0_AXI>; + clock-names = "ipg", "axi"; + fsl,pres = <&pre1>, <&pre2>, <&pre3>; +}; + Parallel display support ======================== diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index ec0bfb9bbebd..4686f4bdaca0 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -178,6 +178,7 @@ maxim Maxim Integrated Products mcube mCube meas Measurement Specialties mediatek MediaTek Inc. +megachips MegaChips melexis Melexis N.V. melfas MELFAS Inc. memsic MEMSIC Inc. diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index e35920db1f4c..3bb4d937cdfe 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -240,9 +240,6 @@ drivers. .. kernel-doc:: drivers/gpu/drm/drm_pci.c :export: -.. kernel-doc:: drivers/gpu/drm/drm_platform.c - :export: - Open/Close, File Operations and IOCTLs ====================================== @@ -298,10 +295,10 @@ over. File Operations --------------- -.. kernel-doc:: drivers/gpu/drm/drm_fops.c +.. kernel-doc:: drivers/gpu/drm/drm_file.c :doc: file operations -.. kernel-doc:: drivers/gpu/drm/drm_fops.c +.. kernel-doc:: drivers/gpu/drm/drm_file.c :export: IOCTLs diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index fd35998acefc..d7a29d41789f 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -449,6 +449,9 @@ PRIME Helper Functions PRIME Function References ------------------------- +.. kernel-doc:: include/drm/drm_prime.h + :internal: + .. kernel-doc:: drivers/gpu/drm/drm_prime.c :export: diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index ce0f1a588e7f..64e9d16170ce 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -153,6 +153,19 @@ following drivers still use ``struct_mutex``: ``msm``, ``omapdrm`` and Contact: Daniel Vetter +Switch to drm_connector_list_iter for any connector_list walking +---------------------------------------------------------------- + +Connectors can be hotplugged, and we now have a special list of helpers to walk +the connector_list in a race-free fashion, without incurring deadlocks on +mutexes and other fun stuff. + +Unfortunately most drivers are not converted yet. At least all those supporting +DP MST hotplug should be converted, since for those drivers the difference +matters. See drm_for_each_connector_iter() vs. drm_for_each_connector(). + +Contact: Daniel Vetter + Core refactorings ================= diff --git a/MAINTAINERS b/MAINTAINERS index 4084e2fa39c9..e88154f2226c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8123,6 +8123,14 @@ L: linux-wireless@vger.kernel.org S: Maintained F: drivers/net/wireless/mediatek/mt7601u/ +MEGACHIPS STDPXXXX-GE-B850V3-FW LVDS/DP++ BRIDGES +M: Peter Senna Tschudin <peter.senna@collabora.com> +M: Martin Donnelly <martin.donnelly@ge.com> +M: Martyn Welch <martyn.welch@collabora.co.uk> +S: Maintained +F: drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +F: Documentation/devicetree/bindings/video/bridge/megachips-stdpxxxx-ge-b850v3-fw.txt + MEGARAID SCSI/SAS DRIVERS M: Kashyap Desai <kashyap.desai@broadcom.com> M: Sumit Saxena <sumit.saxena@broadcom.com> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 3ee95793d122..59aae43005ee 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -4,10 +4,10 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_context.o drm_dma.o \ - drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \ + drm_file.o drm_gem.o drm_ioctl.o drm_irq.o \ drm_lock.o drm_memory.o drm_drv.o \ drm_scatter.o drm_pci.o \ - drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ + drm_sysfs.o drm_hashtab.o drm_mm.o \ drm_crtc.o drm_fourcc.o drm_modes.o drm_edid.o \ drm_info.o drm_encoder_slave.o \ drm_trace_points.o drm_global.o drm_prime.o \ diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 1952e8748fea..408e00b64cf6 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -137,10 +137,9 @@ static int armada_drm_bind(struct device *dev) return ret; } - priv->drm.platformdev = to_platform_device(dev); priv->drm.dev_private = priv; - platform_set_drvdata(priv->drm.platformdev, &priv->drm); + dev_set_drvdata(dev, &priv->drm); INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); INIT_KFIFO(priv->fb_unref); diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index eb8688ec6f18..f6968d3b4b41 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -24,29 +24,25 @@ config DRM_DUMB_VGA_DAC help Support for RGB to VGA DAC based bridges -config DRM_DW_HDMI - tristate +config DRM_LVDS_ENCODER + tristate "Transparent parallel to LVDS encoder support" + depends on OF select DRM_KMS_HELPER - -config DRM_DW_HDMI_AHB_AUDIO - tristate "Synopsis Designware AHB Audio interface" - depends on DRM_DW_HDMI && SND - select SND_PCM - select SND_PCM_ELD - select SND_PCM_IEC958 + select DRM_PANEL help - Support the AHB Audio interface which is part of the Synopsis - Designware HDMI block. This is used in conjunction with - the i.MX6 HDMI driver. + Support for transparent parallel to LVDS encoders that don't require + any configuration. -config DRM_DW_HDMI_I2S_AUDIO - tristate "Synopsis Designware I2S Audio interface" - depends on SND_SOC - depends on DRM_DW_HDMI - select SND_SOC_HDMI_CODEC - help - Support the I2S Audio interface which is part of the Synopsis - Designware HDMI block. +config DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW + tristate "MegaChips stdp4028-ge-b850v3-fw and stdp2690-ge-b850v3-fw" + depends on OF + select DRM_KMS_HELPER + select DRM_PANEL + ---help--- + This is a driver for the display bridges of + GE B850v3 that convert dual channel LVDS + to DP++. This is used with the i.MX6 imx-ldb + driver. You are likely to say N here. config DRM_NXP_PTN3460 tristate "NXP PTN3460 DP/LVDS bridge" @@ -101,4 +97,6 @@ source "drivers/gpu/drm/bridge/analogix/Kconfig" source "drivers/gpu/drm/bridge/adv7511/Kconfig" +source "drivers/gpu/drm/bridge/synopsys/Kconfig" + endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 2e83a7855399..3fe2226ee2f2 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -2,9 +2,8 @@ ccflags-y := -Iinclude/drm obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o -obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o -obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o -obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o +obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o +obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o @@ -13,3 +12,4 @@ obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o +obj-y += synopsys/ diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index e7cd1056ff2d..c26997afd3cf 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1488,6 +1488,28 @@ int analogix_dp_resume(struct device *dev) EXPORT_SYMBOL_GPL(analogix_dp_resume); #endif +int analogix_dp_start_crc(struct drm_connector *connector) +{ + struct analogix_dp_device *dp = to_dp(connector); + + if (!connector->state->crtc) { + DRM_ERROR("Connector %s doesn't currently have a CRTC.\n", + connector->name); + return -EINVAL; + } + + return drm_dp_start_crc(&dp->aux, connector->state->crtc); +} +EXPORT_SYMBOL_GPL(analogix_dp_start_crc); + +int analogix_dp_stop_crc(struct drm_connector *connector) +{ + struct analogix_dp_device *dp = to_dp(connector); + + return drm_dp_stop_crc(&dp->aux); +} +EXPORT_SYMBOL_GPL(analogix_dp_stop_crc); + MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); MODULE_DESCRIPTION("Analogix DP Core Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c index 86e9f9c7b59c..63e113bd21d2 100644 --- a/drivers/gpu/drm/bridge/dumb-vga-dac.c +++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c @@ -237,6 +237,7 @@ static int dumb_vga_remove(struct platform_device *pdev) static const struct of_device_id dumb_vga_match[] = { { .compatible = "dumb-vga-dac" }, + { .compatible = "adi,adv7123" }, { .compatible = "ti,ths8135" }, {}, }; diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c new file mode 100644 index 000000000000..f1f67a279426 --- /dev/null +++ b/drivers/gpu/drm/bridge/lvds-encoder.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_encoder.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_panel.h> + +#include <linux/of_graph.h> + +struct lvds_encoder { + struct device *dev; + + struct drm_bridge bridge; + struct drm_connector connector; + struct drm_panel *panel; +}; + +static inline struct lvds_encoder * +drm_bridge_to_lvds_encoder(struct drm_bridge *bridge) +{ + return container_of(bridge, struct lvds_encoder, bridge); +} + +static inline struct lvds_encoder * +drm_connector_to_lvds_encoder(struct drm_connector *connector) +{ + return container_of(connector, struct lvds_encoder, connector); +} + +static int lvds_connector_get_modes(struct drm_connector *connector) +{ + struct lvds_encoder *lvds = drm_connector_to_lvds_encoder(connector); + + return drm_panel_get_modes(lvds->panel); +} + +static const struct drm_connector_helper_funcs lvds_connector_helper_funcs = { + .get_modes = lvds_connector_get_modes, +}; + +static const struct drm_connector_funcs lvds_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int lvds_encoder_attach(struct drm_bridge *bridge) +{ + struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); + struct drm_connector *connector = &lvds->connector; + int ret; + + if (!bridge->encoder) { + DRM_ERROR("Missing encoder\n"); + return -ENODEV; + } + + drm_connector_helper_add(connector, &lvds_connector_helper_funcs); + + ret = drm_connector_init(bridge->dev, connector, &lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + return ret; + } + + drm_mode_connector_attach_encoder(&lvds->connector, bridge->encoder); + + ret = drm_panel_attach(lvds->panel, &lvds->connector); + if (ret < 0) + return ret; + + return 0; +} + +static void lvds_encoder_detach(struct drm_bridge *bridge) +{ + struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); + + drm_panel_detach(lvds->panel); +} + +static void lvds_encoder_pre_enable(struct drm_bridge *bridge) +{ + struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); + + drm_panel_prepare(lvds->panel); +} + +static void lvds_encoder_enable(struct drm_bridge *bridge) +{ + struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); + + drm_panel_enable(lvds->panel); +} + +static void lvds_encoder_disable(struct drm_bridge *bridge) +{ + struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); + + drm_panel_disable(lvds->panel); +} + +static void lvds_encoder_post_disable(struct drm_bridge *bridge) +{ + struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); + + drm_panel_unprepare(lvds->panel); +} + +static const struct drm_bridge_funcs lvds_encoder_bridge_funcs = { + .attach = lvds_encoder_attach, + .detach = lvds_encoder_detach, + .pre_enable = lvds_encoder_pre_enable, + .enable = lvds_encoder_enable, + .disable = lvds_encoder_disable, + .post_disable = lvds_encoder_post_disable, +}; + +static int lvds_encoder_probe(struct platform_device *pdev) +{ + struct lvds_encoder *lvds; + struct device_node *port; + struct device_node *endpoint; + struct device_node *panel; + + lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); + if (!lvds) + return -ENOMEM; + + lvds->dev = &pdev->dev; + platform_set_drvdata(pdev, lvds); + + lvds->bridge.funcs = &lvds_encoder_bridge_funcs; + lvds->bridge.of_node = pdev->dev.of_node; + + /* Locate the panel DT node. */ + port = of_graph_get_port_by_id(pdev->dev.of_node, 1); + if (!port) { + dev_dbg(&pdev->dev, "port 1 not found\n"); + return -ENXIO; + } + + endpoint = of_get_child_by_name(port, "endpoint"); + of_node_put(port); + if (!endpoint) { + dev_dbg(&pdev->dev, "no endpoint for port 1\n"); + return -ENXIO; + } + + panel = of_graph_get_remote_port_parent(endpoint); + of_node_put(endpoint); + if (!panel) { + dev_dbg(&pdev->dev, "no remote endpoint for port 1\n"); + return -ENXIO; + } + + lvds->panel = of_drm_find_panel(panel); + of_node_put(panel); + if (!lvds->panel) { + dev_dbg(&pdev->dev, "panel not found, deferring probe\n"); + return -EPROBE_DEFER; + } + + /* Register the bridge. */ + return drm_bridge_add(&lvds->bridge); +} + +static int lvds_encoder_remove(struct platform_device *pdev) +{ + struct lvds_encoder *encoder = platform_get_drvdata(pdev); + + drm_bridge_remove(&encoder->bridge); + + return 0; +} + +static const struct of_device_id lvds_encoder_match[] = { + { .compatible = "lvds-encoder" }, + { .compatible = "thine,thc63lvdm83d" }, + {}, +}; +MODULE_DEVICE_TABLE(of, lvds_encoder_match); + +static struct platform_driver lvds_encoder_driver = { + .probe = lvds_encoder_probe, + .remove = lvds_encoder_remove, + .driver = { + .name = "lvds-encoder", + .of_match_table = lvds_encoder_match, + }, +}; +module_platform_driver(lvds_encoder_driver); + +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_DESCRIPTION("Transparent parallel to LVDS encoder"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c new file mode 100644 index 000000000000..cfc606a13a6d --- /dev/null +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -0,0 +1,428 @@ +/* + * Driver for MegaChips STDP4028 with GE B850v3 firmware (LVDS-DP) + * Driver for MegaChips STDP2690 with GE B850v3 firmware (DP-DP++) + + * Copyright (c) 2017, Collabora Ltd. + * Copyright (c) 2017, General Electric Company + + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + + * This driver creates a drm_bridge and a drm_connector for the LVDS to DP++ + * display bridge of the GE B850v3. There are two physical bridges on the video + * signal pipeline: a STDP4028(LVDS to DP) and a STDP2690(DP to DP++). The + * physical bridges are automatically configured by the input video signal, and + * the driver has no access to the video processing pipeline. The driver is + * only needed to read EDID from the STDP2690 and to handle HPD events from the + * STDP4028. The driver communicates with both bridges over i2c. The video + * signal pipeline is as follows: + * + * Host -> LVDS|--(STDP4028)--|DP -> DP|--(STDP2690)--|DP++ -> Video output + * + */ + +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drmP.h> + +#define EDID_EXT_BLOCK_CNT 0x7E + +#define STDP4028_IRQ_OUT_CONF_REG 0x02 +#define STDP4028_DPTX_IRQ_EN_REG 0x3C +#define STDP4028_DPTX_IRQ_STS_REG 0x3D +#define STDP4028_DPTX_STS_REG 0x3E + +#define STDP4028_DPTX_DP_IRQ_EN 0x1000 + +#define STDP4028_DPTX_HOTPLUG_IRQ_EN 0x0400 +#define STDP4028_DPTX_LINK_CH_IRQ_EN 0x2000 +#define STDP4028_DPTX_IRQ_CONFIG \ + (STDP4028_DPTX_LINK_CH_IRQ_EN | STDP4028_DPTX_HOTPLUG_IRQ_EN) + +#define STDP4028_DPTX_HOTPLUG_STS 0x0200 +#define STDP4028_DPTX_LINK_STS 0x1000 +#define STDP4028_CON_STATE_CONNECTED \ + (STDP4028_DPTX_HOTPLUG_STS | STDP4028_DPTX_LINK_STS) + +#define STDP4028_DPTX_HOTPLUG_CH_STS 0x0400 +#define STDP4028_DPTX_LINK_CH_STS 0x2000 +#define STDP4028_DPTX_IRQ_CLEAR \ + (STDP4028_DPTX_LINK_CH_STS | STDP4028_DPTX_HOTPLUG_CH_STS) + +static DEFINE_MUTEX(ge_b850v3_lvds_dev_mutex); + +struct ge_b850v3_lvds { + struct drm_connector connector; + struct drm_bridge bridge; + struct i2c_client *stdp4028_i2c; + struct i2c_client *stdp2690_i2c; + struct edid *edid; +}; + +static struct ge_b850v3_lvds *ge_b850v3_lvds_ptr; + +static u8 *stdp2690_get_edid(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + unsigned char start = 0x00; + unsigned int total_size; + u8 *block = kmalloc(EDID_LENGTH, GFP_KERNEL); + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &start, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = EDID_LENGTH, + .buf = block, + } + }; + + if (!block) + return NULL; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + DRM_ERROR("Unable to read EDID.\n"); + goto err; + } + + if (!drm_edid_block_valid(block, 0, false, NULL)) { + DRM_ERROR("Invalid EDID data\n"); + goto err; + } + + total_size = (block[EDID_EXT_BLOCK_CNT] + 1) * EDID_LENGTH; + if (total_size > EDID_LENGTH) { + kfree(block); + block = kmalloc(total_size, GFP_KERNEL); + if (!block) + return NULL; + + /* Yes, read the entire buffer, and do not skip the first + * EDID_LENGTH bytes. + */ + start = 0x00; + msgs[1].len = total_size; + msgs[1].buf = block; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + DRM_ERROR("Unable to read EDID extension blocks.\n"); + goto err; + } + if (!drm_edid_block_valid(block, 1, false, NULL)) { + DRM_ERROR("Invalid EDID data\n"); + goto err; + } + } + + return block; + +err: + kfree(block); + return NULL; +} + +static int ge_b850v3_lvds_get_modes(struct drm_connector *connector) +{ + struct i2c_client *client; + int num_modes = 0; + + client = ge_b850v3_lvds_ptr->stdp2690_i2c; + + kfree(ge_b850v3_lvds_ptr->edid); + ge_b850v3_lvds_ptr->edid = (struct edid *)stdp2690_get_edid(client); + + if (ge_b850v3_lvds_ptr->edid) { + drm_mode_connector_update_edid_property(connector, + ge_b850v3_lvds_ptr->edid); + num_modes = drm_add_edid_modes(connector, + ge_b850v3_lvds_ptr->edid); + } + + return num_modes; +} + +static enum drm_mode_status ge_b850v3_lvds_mode_valid( + struct drm_connector *connector, struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct +drm_connector_helper_funcs ge_b850v3_lvds_connector_helper_funcs = { + .get_modes = ge_b850v3_lvds_get_modes, + .mode_valid = ge_b850v3_lvds_mode_valid, +}; + +static enum drm_connector_status ge_b850v3_lvds_detect( + struct drm_connector *connector, bool force) +{ + struct i2c_client *stdp4028_i2c = + ge_b850v3_lvds_ptr->stdp4028_i2c; + s32 link_state; + + link_state = i2c_smbus_read_word_data(stdp4028_i2c, + STDP4028_DPTX_STS_REG); + + if (link_state == STDP4028_CON_STATE_CONNECTED) + return connector_status_connected; + + if (link_state == 0) + return connector_status_disconnected; + + return connector_status_unknown; +} + +static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = ge_b850v3_lvds_detect, + .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 irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id) +{ + struct i2c_client *stdp4028_i2c + = ge_b850v3_lvds_ptr->stdp4028_i2c; + + i2c_smbus_write_word_data(stdp4028_i2c, + STDP4028_DPTX_IRQ_STS_REG, + STDP4028_DPTX_IRQ_CLEAR); + + if (ge_b850v3_lvds_ptr->connector.dev) + drm_kms_helper_hotplug_event(ge_b850v3_lvds_ptr->connector.dev); + + return IRQ_HANDLED; +} + +static int ge_b850v3_lvds_attach(struct drm_bridge *bridge) +{ + struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector; + struct i2c_client *stdp4028_i2c + = ge_b850v3_lvds_ptr->stdp4028_i2c; + int ret; + + if (!bridge->encoder) { + DRM_ERROR("Parent encoder object not found"); + return -ENODEV; + } + + connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_helper_add(connector, + &ge_b850v3_lvds_connector_helper_funcs); + + ret = drm_connector_init(bridge->dev, connector, + &ge_b850v3_lvds_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + + ret = drm_mode_connector_attach_encoder(connector, bridge->encoder); + if (ret) + return ret; + + /* Configures the bridge to re-enable interrupts after each ack. */ + i2c_smbus_write_word_data(stdp4028_i2c, + STDP4028_IRQ_OUT_CONF_REG, + STDP4028_DPTX_DP_IRQ_EN); + + /* Enable interrupts */ + i2c_smbus_write_word_data(stdp4028_i2c, + STDP4028_DPTX_IRQ_EN_REG, + STDP4028_DPTX_IRQ_CONFIG); + + return 0; +} + +static const struct drm_bridge_funcs ge_b850v3_lvds_funcs = { + .attach = ge_b850v3_lvds_attach, +}; + +static int ge_b850v3_lvds_init(struct device *dev) +{ + mutex_lock(&ge_b850v3_lvds_dev_mutex); + + if (ge_b850v3_lvds_ptr) + goto success; + + ge_b850v3_lvds_ptr = devm_kzalloc(dev, + sizeof(*ge_b850v3_lvds_ptr), + GFP_KERNEL); + + if (!ge_b850v3_lvds_ptr) { + mutex_unlock(&ge_b850v3_lvds_dev_mutex); + return -ENOMEM; + } + + ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs; + ge_b850v3_lvds_ptr->bridge.of_node = dev->of_node; + drm_bridge_add(&ge_b850v3_lvds_ptr->bridge); + +success: + mutex_unlock(&ge_b850v3_lvds_dev_mutex); + return 0; +} + +static void ge_b850v3_lvds_remove(void) +{ + mutex_lock(&ge_b850v3_lvds_dev_mutex); + /* + * This check is to avoid both the drivers + * removing the bridge in their remove() function + */ + if (!ge_b850v3_lvds_ptr) + goto out; + + drm_bridge_remove(&ge_b850v3_lvds_ptr->bridge); + + kfree(ge_b850v3_lvds_ptr->edid); + + ge_b850v3_lvds_ptr = NULL; +out: + mutex_unlock(&ge_b850v3_lvds_dev_mutex); +} + +static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &stdp4028_i2c->dev; + + ge_b850v3_lvds_init(dev); + + ge_b850v3_lvds_ptr->stdp4028_i2c = stdp4028_i2c; + i2c_set_clientdata(stdp4028_i2c, ge_b850v3_lvds_ptr); + + /* Clear pending interrupts since power up. */ + i2c_smbus_write_word_data(stdp4028_i2c, + STDP4028_DPTX_IRQ_STS_REG, + STDP4028_DPTX_IRQ_CLEAR); + + if (!stdp4028_i2c->irq) + return 0; + + return devm_request_threaded_irq(&stdp4028_i2c->dev, + stdp4028_i2c->irq, NULL, + ge_b850v3_lvds_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr); +} + +static int stdp4028_ge_b850v3_fw_remove(struct i2c_client *stdp4028_i2c) +{ + ge_b850v3_lvds_remove(); + + return 0; +} + +static const struct i2c_device_id stdp4028_ge_b850v3_fw_i2c_table[] = { + {"stdp4028_ge_fw", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, stdp4028_ge_b850v3_fw_i2c_table); + +static const struct of_device_id stdp4028_ge_b850v3_fw_match[] = { + { .compatible = "megachips,stdp4028-ge-b850v3-fw" }, + {}, +}; +MODULE_DEVICE_TABLE(of, stdp4028_ge_b850v3_fw_match); + +static struct i2c_driver stdp4028_ge_b850v3_fw_driver = { + .id_table = stdp4028_ge_b850v3_fw_i2c_table, + .probe = stdp4028_ge_b850v3_fw_probe, + .remove = stdp4028_ge_b850v3_fw_remove, + .driver = { + .name = "stdp4028-ge-b850v3-fw", + .of_match_table = stdp4028_ge_b850v3_fw_match, + }, +}; + +static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &stdp2690_i2c->dev; + + ge_b850v3_lvds_init(dev); + + ge_b850v3_lvds_ptr->stdp2690_i2c = stdp2690_i2c; + i2c_set_clientdata(stdp2690_i2c, ge_b850v3_lvds_ptr); + + return 0; +} + +static int stdp2690_ge_b850v3_fw_remove(struct i2c_client *stdp2690_i2c) +{ + ge_b850v3_lvds_remove(); + + return 0; +} + +static const struct i2c_device_id stdp2690_ge_b850v3_fw_i2c_table[] = { + {"stdp2690_ge_fw", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, stdp2690_ge_b850v3_fw_i2c_table); + +static const struct of_device_id stdp2690_ge_b850v3_fw_match[] = { + { .compatible = "megachips,stdp2690-ge-b850v3-fw" }, + {}, +}; +MODULE_DEVICE_TABLE(of, stdp2690_ge_b850v3_fw_match); + +static struct i2c_driver stdp2690_ge_b850v3_fw_driver = { + .id_table = stdp2690_ge_b850v3_fw_i2c_table, + .probe = stdp2690_ge_b850v3_fw_probe, + .remove = stdp2690_ge_b850v3_fw_remove, + .driver = { + .name = "stdp2690-ge-b850v3-fw", + .of_match_table = stdp2690_ge_b850v3_fw_match, + }, +}; + +static int __init stdpxxxx_ge_b850v3_init(void) +{ + int ret; + + ret = i2c_add_driver(&stdp4028_ge_b850v3_fw_driver); + if (ret) + return ret; + + return i2c_add_driver(&stdp2690_ge_b850v3_fw_driver); +} +module_init(stdpxxxx_ge_b850v3_init); + +static void __exit stdpxxxx_ge_b850v3_exit(void) +{ + i2c_del_driver(&stdp2690_ge_b850v3_fw_driver); + i2c_del_driver(&stdp4028_ge_b850v3_fw_driver); +} +module_exit(stdpxxxx_ge_b850v3_exit); + +MODULE_AUTHOR("Peter Senna Tschudin <peter.senna@collabora.com>"); +MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.co.uk>"); +MODULE_DESCRIPTION("GE LVDS to DP++ display bridge)"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/bridge/synopsys/Kconfig b/drivers/gpu/drm/bridge/synopsys/Kconfig new file mode 100644 index 000000000000..40d2827a6d19 --- /dev/null +++ b/drivers/gpu/drm/bridge/synopsys/Kconfig @@ -0,0 +1,23 @@ +config DRM_DW_HDMI + tristate + select DRM_KMS_HELPER + +config DRM_DW_HDMI_AHB_AUDIO + tristate "Synopsys Designware AHB Audio interface" + depends on DRM_DW_HDMI && SND + select SND_PCM + select SND_PCM_ELD + select SND_PCM_IEC958 + help + Support the AHB Audio interface which is part of the Synopsys + Designware HDMI block. This is used in conjunction with + the i.MX6 HDMI driver. + +config DRM_DW_HDMI_I2S_AUDIO + tristate "Synopsys Designware I2S Audio interface" + depends on SND_SOC + depends on DRM_DW_HDMI + select SND_SOC_HDMI_CODEC + help + Support the I2S Audio interface which is part of the Synopsys + Designware HDMI block. diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile new file mode 100644 index 000000000000..17aa7a65b57e --- /dev/null +++ b/drivers/gpu/drm/bridge/synopsys/Makefile @@ -0,0 +1,5 @@ +#ccflags-y := -Iinclude/drm + +obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o +obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o +obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o diff --git a/drivers/gpu/drm/bridge/dw-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c index 8f2d1379c880..8f2d1379c880 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi-ahb-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c diff --git a/drivers/gpu/drm/bridge/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h index fd1f745c6073..fd1f745c6073 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi-audio.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h diff --git a/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index aaf287d2e91d..aaf287d2e91d 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 9a9ec27d9e28..026a0dce7661 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -19,6 +19,7 @@ #include <linux/hdmi.h> #include <linux/mutex.h> #include <linux/of_device.h> +#include <linux/regmap.h> #include <linux/spinlock.h> #include <drm/drm_of.h> @@ -116,14 +117,17 @@ struct dw_hdmi_i2c { struct dw_hdmi_phy_data { enum dw_hdmi_phy_type type; const char *name; + unsigned int gen; bool has_svsret; + int (*configure)(struct dw_hdmi *hdmi, + const struct dw_hdmi_plat_data *pdata, + unsigned long mpixelclock); }; struct dw_hdmi { struct drm_connector connector; struct drm_bridge bridge; - enum dw_hdmi_devtype dev_type; unsigned int version; struct platform_device *audio; @@ -140,8 +144,12 @@ struct dw_hdmi { u8 edid[HDMI_EDID_LEN]; bool cable_plugin; - const struct dw_hdmi_phy_data *phy; - bool phy_enabled; + struct { + const struct dw_hdmi_phy_ops *ops; + const char *name; + void *data; + bool enabled; + } phy; struct drm_display_mode previous_mode; @@ -164,8 +172,8 @@ struct dw_hdmi { unsigned int audio_n; bool audio_enable; - void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); - u8 (*read)(struct dw_hdmi *hdmi, int offset); + unsigned int reg_shift; + struct regmap *regm; }; #define HDMI_IH_PHY_STAT0_RX_SENSE \ @@ -176,42 +184,23 @@ struct dw_hdmi { (HDMI_PHY_RX_SENSE0 | HDMI_PHY_RX_SENSE1 | \ HDMI_PHY_RX_SENSE2 | HDMI_PHY_RX_SENSE3) -static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset) -{ - writel(val, hdmi->regs + (offset << 2)); -} - -static u8 dw_hdmi_readl(struct dw_hdmi *hdmi, int offset) -{ - return readl(hdmi->regs + (offset << 2)); -} - -static void dw_hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) -{ - writeb(val, hdmi->regs + offset); -} - -static u8 dw_hdmi_readb(struct dw_hdmi *hdmi, int offset) -{ - return readb(hdmi->regs + offset); -} - static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) { - hdmi->write(hdmi, val, offset); + regmap_write(hdmi->regm, offset << hdmi->reg_shift, val); } static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset) { - return hdmi->read(hdmi, offset); + unsigned int val = 0; + + regmap_read(hdmi->regm, offset << hdmi->reg_shift, &val); + + return val; } static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg) { - u8 val = hdmi_readb(hdmi, reg) & ~mask; - - val |= data & mask; - hdmi_writeb(hdmi, val, reg); + regmap_update_bits(hdmi->regm, reg << hdmi->reg_shift, mask, data); } static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, @@ -830,6 +819,10 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) HDMI_VP_CONF); } +/* ----------------------------------------------------------------------------- + * Synopsys PHY Handling + */ + static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, unsigned char bit) { @@ -837,32 +830,6 @@ static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0); } -static inline void hdmi_phy_test_enable(struct dw_hdmi *hdmi, - unsigned char bit) -{ - hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET, - HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0); -} - -static inline void hdmi_phy_test_clock(struct dw_hdmi *hdmi, - unsigned char bit) -{ - hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET, - HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0); -} - -static inline void hdmi_phy_test_din(struct dw_hdmi *hdmi, - unsigned char bit) -{ - hdmi_writeb(hdmi, bit, HDMI_PHY_TST1); -} - -static inline void hdmi_phy_test_dout(struct dw_hdmi *hdmi, - unsigned char bit) -{ - hdmi_writeb(hdmi, bit, HDMI_PHY_TST2); -} - static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec) { u32 val; @@ -877,8 +844,8 @@ static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec) return true; } -static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, - unsigned char addr) +void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, + unsigned char addr) { hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR); @@ -890,6 +857,7 @@ static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, HDMI_PHY_I2CM_OPERATION_ADDR); hdmi_phy_wait_i2c_done(hdmi, 1000); } +EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write); static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable) { @@ -940,54 +908,142 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable) HDMI_PHY_CONF0_SELDIPIF_MASK); } -static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon) +static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi) +{ + const struct dw_hdmi_phy_data *phy = hdmi->phy.data; + unsigned int i; + u16 val; + + if (phy->gen == 1) { + dw_hdmi_phy_enable_tmds(hdmi, 0); + dw_hdmi_phy_enable_powerdown(hdmi, true); + return; + } + + dw_hdmi_phy_gen2_txpwron(hdmi, 0); + + /* + * Wait for TX_PHY_LOCK to be deasserted to indicate that the PHY went + * to low power mode. + */ + for (i = 0; i < 5; ++i) { + val = hdmi_readb(hdmi, HDMI_PHY_STAT0); + if (!(val & HDMI_PHY_TX_PHY_LOCK)) + break; + + usleep_range(1000, 2000); + } + + if (val & HDMI_PHY_TX_PHY_LOCK) + dev_warn(hdmi->dev, "PHY failed to power down\n"); + else + dev_dbg(hdmi->dev, "PHY powered down in %u iterations\n", i); + + dw_hdmi_phy_gen2_pddq(hdmi, 1); +} + +static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi) +{ + const struct dw_hdmi_phy_data *phy = hdmi->phy.data; + unsigned int i; + u8 val; + + if (phy->gen == 1) { + dw_hdmi_phy_enable_powerdown(hdmi, false); + + /* Toggle TMDS enable. */ + dw_hdmi_phy_enable_tmds(hdmi, 0); + dw_hdmi_phy_enable_tmds(hdmi, 1); + return 0; + } + + dw_hdmi_phy_gen2_txpwron(hdmi, 1); + dw_hdmi_phy_gen2_pddq(hdmi, 0); + + /* Wait for PHY PLL lock */ + for (i = 0; i < 5; ++i) { + val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; + if (val) + break; + + usleep_range(1000, 2000); + } + + if (!val) { + dev_err(hdmi->dev, "PHY PLL failed to lock\n"); + return -ETIMEDOUT; + } + + dev_dbg(hdmi->dev, "PHY PLL locked %u iterations\n", i); + return 0; +} + +/* + * PHY configuration function for the DWC HDMI 3D TX PHY. Based on the available + * information the DWC MHL PHY has the same register layout and is thus also + * supported by this function. + */ +static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi, + const struct dw_hdmi_plat_data *pdata, + unsigned long mpixelclock) { - u8 val, msec; - const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg; const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; /* PLL/MPLL Cfg - always match on final entry */ for (; mpll_config->mpixelclock != ~0UL; mpll_config++) - if (hdmi->hdmi_data.video_mode.mpixelclock <= - mpll_config->mpixelclock) + if (mpixelclock <= mpll_config->mpixelclock) break; for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++) - if (hdmi->hdmi_data.video_mode.mpixelclock <= - curr_ctrl->mpixelclock) + if (mpixelclock <= curr_ctrl->mpixelclock) break; for (; phy_config->mpixelclock != ~0UL; phy_config++) - if (hdmi->hdmi_data.video_mode.mpixelclock <= - phy_config->mpixelclock) + if (mpixelclock <= phy_config->mpixelclock) break; if (mpll_config->mpixelclock == ~0UL || curr_ctrl->mpixelclock == ~0UL || - phy_config->mpixelclock == ~0UL) { - dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n", - hdmi->hdmi_data.video_mode.mpixelclock); + phy_config->mpixelclock == ~0UL) return -EINVAL; - } - /* Enable csc path */ - if (cscon) - val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH; - else - val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS; + dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce, + HDMI_3D_TX_PHY_CPCE_CTRL); + dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, + HDMI_3D_TX_PHY_GMPCTRL); + dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], + HDMI_3D_TX_PHY_CURRCTRL); - hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL); + dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL); + dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK, + HDMI_3D_TX_PHY_MSM_CTRL); - /* gen2 tx power off */ - dw_hdmi_phy_gen2_txpwron(hdmi, 0); + dw_hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM); + dw_hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, + HDMI_3D_TX_PHY_CKSYMTXCTRL); + dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, + HDMI_3D_TX_PHY_VLEVCTRL); - /* gen2 pddq */ - dw_hdmi_phy_gen2_pddq(hdmi, 1); + /* Override and disable clock termination. */ + dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE, + HDMI_3D_TX_PHY_CKCALCTRL); + + return 0; +} + +static int hdmi_phy_configure(struct dw_hdmi *hdmi) +{ + const struct dw_hdmi_phy_data *phy = hdmi->phy.data; + const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; + unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock; + int ret; + + dw_hdmi_phy_power_off(hdmi); /* Leave low power consumption mode by asserting SVSRET. */ - if (hdmi->phy->has_svsret) + if (phy->has_svsret) dw_hdmi_phy_enable_svsret(hdmi, 1); /* PHY reset. The reset signal is active high on Gen2 PHYs. */ @@ -1001,81 +1057,60 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon) HDMI_PHY_I2CM_SLAVE_ADDR); hdmi_phy_test_clear(hdmi, 0); - hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce, - HDMI_3D_TX_PHY_CPCE_CTRL); - hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, - HDMI_3D_TX_PHY_GMPCTRL); - hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], - HDMI_3D_TX_PHY_CURRCTRL); - - hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL); - hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK, - HDMI_3D_TX_PHY_MSM_CTRL); - - hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM); - hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, - HDMI_3D_TX_PHY_CKSYMTXCTRL); - hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, - HDMI_3D_TX_PHY_VLEVCTRL); - - /* Override and disable clock termination. */ - hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE, - HDMI_3D_TX_PHY_CKCALCTRL); - - dw_hdmi_phy_enable_powerdown(hdmi, false); - - /* toggle TMDS enable */ - dw_hdmi_phy_enable_tmds(hdmi, 0); - dw_hdmi_phy_enable_tmds(hdmi, 1); - - /* gen2 tx power on */ - dw_hdmi_phy_gen2_txpwron(hdmi, 1); - dw_hdmi_phy_gen2_pddq(hdmi, 0); - - /* Wait for PHY PLL lock */ - msec = 5; - do { - val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; - if (!val) - break; - - if (msec == 0) { - dev_err(hdmi->dev, "PHY PLL not locked\n"); - return -ETIMEDOUT; - } - - udelay(1000); - msec--; - } while (1); + /* Write to the PHY as configured by the platform */ + if (pdata->configure_phy) + ret = pdata->configure_phy(hdmi, pdata, mpixelclock); + else + ret = phy->configure(hdmi, pdata, mpixelclock); + if (ret) { + dev_err(hdmi->dev, "PHY configuration failed (clock %lu)\n", + mpixelclock); + return ret; + } - return 0; + return dw_hdmi_phy_power_on(hdmi); } -static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) +static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, + struct drm_display_mode *mode) { int i, ret; - bool cscon; - - /*check csc whether needed activated in HDMI mode */ - cscon = hdmi->sink_is_hdmi && is_color_space_conversion(hdmi); /* HDMI Phy spec says to do the phy initialization sequence twice */ for (i = 0; i < 2; i++) { dw_hdmi_phy_sel_data_en_pol(hdmi, 1); dw_hdmi_phy_sel_interface_control(hdmi, 0); - dw_hdmi_phy_enable_tmds(hdmi, 0); - dw_hdmi_phy_enable_powerdown(hdmi, true); - /* Enable CSC */ - ret = hdmi_phy_configure(hdmi, cscon); + ret = hdmi_phy_configure(hdmi); if (ret) return ret; } - hdmi->phy_enabled = true; return 0; } +static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) +{ + dw_hdmi_phy_power_off(hdmi); +} + +static enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, + void *data) +{ + return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? + connector_status_connected : connector_status_disconnected; +} + +static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = { + .init = dw_hdmi_phy_init, + .disable = dw_hdmi_phy_disable, + .read_hpd = dw_hdmi_phy_read_hpd, +}; + +/* ----------------------------------------------------------------------------- + * HDMI TX Setup + */ + static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi) { u8 de; @@ -1290,17 +1325,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH); } -static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi) -{ - if (!hdmi->phy_enabled) - return; - - dw_hdmi_phy_enable_tmds(hdmi, 0); - dw_hdmi_phy_enable_powerdown(hdmi, true); - - hdmi->phy_enabled = false; -} - /* HDMI Initialization Step B.4 */ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) { @@ -1329,6 +1353,14 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); } + + /* Enable color space conversion if needed */ + if (is_color_space_conversion(hdmi)) + hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH, + HDMI_MC_FLOWCTRL); + else + hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS, + HDMI_MC_FLOWCTRL); } static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi) @@ -1425,9 +1457,10 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) hdmi_av_composer(hdmi, mode); /* HDMI Initializateion Step B.2 */ - ret = dw_hdmi_phy_init(hdmi); + ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, &hdmi->previous_mode); if (ret) return ret; + hdmi->phy.enabled = true; /* HDMI Initialization Step B.3 */ dw_hdmi_enable_video_path(hdmi); @@ -1542,7 +1575,11 @@ static void dw_hdmi_poweron(struct dw_hdmi *hdmi) static void dw_hdmi_poweroff(struct dw_hdmi *hdmi) { - dw_hdmi_phy_disable(hdmi); + if (hdmi->phy.enabled) { + hdmi->phy.ops->disable(hdmi, hdmi->phy.data); + hdmi->phy.enabled = false; + } + hdmi->bridge_is_on = false; } @@ -1605,8 +1642,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force) dw_hdmi_update_phy_mask(hdmi); mutex_unlock(&hdmi->mutex); - return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? - connector_status_connected : connector_status_disconnected; + return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); } static int dw_hdmi_connector_get_modes(struct drm_connector *connector) @@ -1858,24 +1894,37 @@ static const struct dw_hdmi_phy_data dw_hdmi_phys[] = { { .type = DW_HDMI_PHY_DWC_HDMI_TX_PHY, .name = "DWC HDMI TX PHY", + .gen = 1, }, { .type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC, .name = "DWC MHL PHY + HEAC PHY", + .gen = 2, .has_svsret = true, + .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, }, { .type = DW_HDMI_PHY_DWC_MHL_PHY, .name = "DWC MHL PHY", + .gen = 2, .has_svsret = true, + .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, }, { .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC, .name = "DWC HDMI 3D TX PHY + HEAC PHY", + .gen = 2, + .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, }, { .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY, .name = "DWC HDMI 3D TX PHY", + .gen = 2, + .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, }, { .type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY, .name = "DWC HDMI 2.0 TX PHY", + .gen = 2, .has_svsret = true, + }, { + .type = DW_HDMI_PHY_VENDOR_PHY, + .name = "Vendor PHY", } }; @@ -1886,22 +1935,56 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID); + if (phy_type == DW_HDMI_PHY_VENDOR_PHY) { + /* Vendor PHYs require support from the glue layer. */ + if (!hdmi->plat_data->phy_ops || !hdmi->plat_data->phy_name) { + dev_err(hdmi->dev, + "Vendor HDMI PHY not supported by glue layer\n"); + return -ENODEV; + } + + hdmi->phy.ops = hdmi->plat_data->phy_ops; + hdmi->phy.data = hdmi->plat_data->phy_data; + hdmi->phy.name = hdmi->plat_data->phy_name; + return 0; + } + + /* Synopsys PHYs are handled internally. */ for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) { if (dw_hdmi_phys[i].type == phy_type) { - hdmi->phy = &dw_hdmi_phys[i]; + hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops; + hdmi->phy.name = dw_hdmi_phys[i].name; + hdmi->phy.data = (void *)&dw_hdmi_phys[i]; + + if (!dw_hdmi_phys[i].configure && + !hdmi->plat_data->configure_phy) { + dev_err(hdmi->dev, "%s requires platform support\n", + hdmi->phy.name); + return -ENODEV; + } + return 0; } } - if (phy_type == DW_HDMI_PHY_VENDOR_PHY) - dev_err(hdmi->dev, "Unsupported vendor HDMI PHY\n"); - else - dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", - phy_type); - + dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", phy_type); return -ENODEV; } +static const struct regmap_config hdmi_regmap_8bit_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_stride = 1, + .max_register = HDMI_I2CM_FS_SCL_LCNT_0_ADDR, +}; + +static const struct regmap_config hdmi_regmap_32bit_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = HDMI_I2CM_FS_SCL_LCNT_0_ADDR << 2, +}; + static struct dw_hdmi * __dw_hdmi_probe(struct platform_device *pdev, const struct dw_hdmi_plat_data *plat_data) @@ -1911,7 +1994,7 @@ __dw_hdmi_probe(struct platform_device *pdev, struct platform_device_info pdevinfo; struct device_node *ddc_node; struct dw_hdmi *hdmi; - struct resource *iores; + struct resource *iores = NULL; int irq; int ret; u32 val = 1; @@ -1926,7 +2009,6 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->plat_data = plat_data; hdmi->dev = dev; - hdmi->dev_type = plat_data->dev_type; hdmi->sample_rate = 48000; hdmi->disabled = true; hdmi->rxsense = true; @@ -1936,22 +2018,6 @@ __dw_hdmi_probe(struct platform_device *pdev, mutex_init(&hdmi->audio_mutex); spin_lock_init(&hdmi->audio_lock); - of_property_read_u32(np, "reg-io-width", &val); - - switch (val) { - case 4: - hdmi->write = dw_hdmi_writel; - hdmi->read = dw_hdmi_readl; - break; - case 1: - hdmi->write = dw_hdmi_writeb; - hdmi->read = dw_hdmi_readb; - break; - default: - dev_err(dev, "reg-io-width must be 1 or 4\n"); - return ERR_PTR(-EINVAL); - } - ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); if (ddc_node) { hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node); @@ -1965,11 +2031,38 @@ __dw_hdmi_probe(struct platform_device *pdev, dev_dbg(hdmi->dev, "no ddc property found\n"); } - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hdmi->regs = devm_ioremap_resource(dev, iores); - if (IS_ERR(hdmi->regs)) { - ret = PTR_ERR(hdmi->regs); - goto err_res; + if (!plat_data->regm) { + const struct regmap_config *reg_config; + + of_property_read_u32(np, "reg-io-width", &val); + switch (val) { + case 4: + reg_config = &hdmi_regmap_32bit_config; + hdmi->reg_shift = 2; + break; + case 1: + reg_config = &hdmi_regmap_8bit_config; + break; + default: + dev_err(dev, "reg-io-width must be 1 or 4\n"); + return ERR_PTR(-EINVAL); + } + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hdmi->regs = devm_ioremap_resource(dev, iores); + if (IS_ERR(hdmi->regs)) { + ret = PTR_ERR(hdmi->regs); + goto err_res; + } + + hdmi->regm = devm_regmap_init_mmio(dev, hdmi->regs, reg_config); + if (IS_ERR(hdmi->regm)) { + dev_err(dev, "Failed to configure regmap\n"); + ret = PTR_ERR(hdmi->regm); + goto err_res; + } + } else { + hdmi->regm = plat_data->regm; } hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr"); @@ -2019,7 +2112,7 @@ __dw_hdmi_probe(struct platform_device *pdev, dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP (%s)\n", hdmi->version >> 12, hdmi->version & 0xfff, prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without", - hdmi->phy->name); + hdmi->phy.name); initialize_hdmi_ih_mutes(hdmi); @@ -2079,7 +2172,7 @@ __dw_hdmi_probe(struct platform_device *pdev, config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID); config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID); - if (config3 & HDMI_CONFIG3_AHBAUDDMA) { + if (iores && config3 & HDMI_CONFIG3_AHBAUDDMA) { struct dw_hdmi_audio_data audio; audio.phys = iores->start; diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index 325b0b8ae639..325b0b8ae639 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 236d947011f9..9b892af7811a 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1374,8 +1374,8 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, return 0; if (conn_state->crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(conn_state->state, - conn_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(conn_state->state, + conn_state->crtc); crtc_state->connector_mask &= ~(1 << drm_connector_index(conn_state->connector)); @@ -1492,7 +1492,7 @@ drm_atomic_add_affected_planes(struct drm_atomic_state *state, { struct drm_plane *plane; - WARN_ON(!drm_atomic_get_existing_crtc_state(state, crtc)); + WARN_ON(!drm_atomic_get_new_crtc_state(state, crtc)); drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) { struct drm_plane_state *plane_state = diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 6b12396f718b..4e26b73bb0d5 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -63,14 +63,15 @@ */ static void drm_atomic_helper_plane_changed(struct drm_atomic_state *state, + struct drm_plane_state *old_plane_state, struct drm_plane_state *plane_state, struct drm_plane *plane) { struct drm_crtc_state *crtc_state; - if (plane->state->crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(state, - plane->state->crtc); + if (old_plane_state->crtc) { + crtc_state = drm_atomic_get_new_crtc_state(state, + old_plane_state->crtc); if (WARN_ON(!crtc_state)) return; @@ -79,8 +80,7 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, } if (plane_state->crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(state, - plane_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); if (WARN_ON(!crtc_state)) return; @@ -92,7 +92,7 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, static int handle_conflicting_encoders(struct drm_atomic_state *state, bool disable_conflicting_encoders) { - struct drm_connector_state *conn_state; + struct drm_connector_state *new_conn_state; struct drm_connector *connector; struct drm_connector_list_iter conn_iter; struct drm_encoder *encoder; @@ -104,15 +104,15 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state, * part of the state. If the same encoder is assigned to multiple * connectors bail out. */ - for_each_connector_in_state(state, connector, conn_state, i) { + for_each_new_connector_in_state(state, connector, new_conn_state, i) { const struct drm_connector_helper_funcs *funcs = connector->helper_private; struct drm_encoder *new_encoder; - if (!conn_state->crtc) + if (!new_conn_state->crtc) continue; if (funcs->atomic_best_encoder) - new_encoder = funcs->atomic_best_encoder(connector, conn_state); + new_encoder = funcs->atomic_best_encoder(connector, new_conn_state); else if (funcs->best_encoder) new_encoder = funcs->best_encoder(connector); else @@ -149,7 +149,7 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state, drm_for_each_connector_iter(connector, &conn_iter) { struct drm_crtc_state *crtc_state; - if (drm_atomic_get_existing_connector_state(state, connector)) + if (drm_atomic_get_new_connector_state(state, connector)) continue; encoder = connector->state->best_encoder; @@ -166,20 +166,20 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state, goto out; } - conn_state = drm_atomic_get_connector_state(state, connector); - if (IS_ERR(conn_state)) { - ret = PTR_ERR(conn_state); + new_conn_state = drm_atomic_get_connector_state(state, connector); + if (IS_ERR(new_conn_state)) { + ret = PTR_ERR(new_conn_state); goto out; } DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], disabling [CONNECTOR:%d:%s]\n", encoder->base.id, encoder->name, - conn_state->crtc->base.id, conn_state->crtc->name, + new_conn_state->crtc->base.id, new_conn_state->crtc->name, connector->base.id, connector->name); - crtc_state = drm_atomic_get_existing_crtc_state(state, conn_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc); - ret = drm_atomic_set_crtc_for_connector(conn_state, NULL); + ret = drm_atomic_set_crtc_for_connector(new_conn_state, NULL); if (ret) goto out; @@ -218,7 +218,7 @@ set_best_encoder(struct drm_atomic_state *state, */ WARN_ON(!crtc && encoder != conn_state->best_encoder); if (crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); crtc_state->encoder_mask &= ~(1 << drm_encoder_index(conn_state->best_encoder)); @@ -229,7 +229,7 @@ set_best_encoder(struct drm_atomic_state *state, crtc = conn_state->crtc; WARN_ON(!crtc); if (crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); crtc_state->encoder_mask |= 1 << drm_encoder_index(encoder); @@ -245,24 +245,24 @@ steal_encoder(struct drm_atomic_state *state, { struct drm_crtc_state *crtc_state; struct drm_connector *connector; - struct drm_connector_state *connector_state; + struct drm_connector_state *old_connector_state, *new_connector_state; int i; - for_each_connector_in_state(state, connector, connector_state, i) { + for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) { struct drm_crtc *encoder_crtc; - if (connector_state->best_encoder != encoder) + if (new_connector_state->best_encoder != encoder) continue; - encoder_crtc = connector->state->crtc; + encoder_crtc = old_connector_state->crtc; DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], stealing it\n", encoder->base.id, encoder->name, encoder_crtc->base.id, encoder_crtc->name); - set_best_encoder(state, connector_state, NULL); + set_best_encoder(state, new_connector_state, NULL); - crtc_state = drm_atomic_get_existing_crtc_state(state, encoder_crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, encoder_crtc); crtc_state->connectors_changed = true; return; @@ -272,7 +272,8 @@ steal_encoder(struct drm_atomic_state *state, static int update_connector_routing(struct drm_atomic_state *state, struct drm_connector *connector, - struct drm_connector_state *connector_state) + struct drm_connector_state *old_connector_state, + struct drm_connector_state *new_connector_state) { const struct drm_connector_helper_funcs *funcs; struct drm_encoder *new_encoder; @@ -282,24 +283,24 @@ update_connector_routing(struct drm_atomic_state *state, connector->base.id, connector->name); - if (connector->state->crtc != connector_state->crtc) { - if (connector->state->crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(state, connector->state->crtc); + if (old_connector_state->crtc != new_connector_state->crtc) { + if (old_connector_state->crtc) { + crtc_state = drm_atomic_get_new_crtc_state(state, old_connector_state->crtc); crtc_state->connectors_changed = true; } - if (connector_state->crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(state, connector_state->crtc); + if (new_connector_state->crtc) { + crtc_state = drm_atomic_get_new_crtc_state(state, new_connector_state->crtc); crtc_state->connectors_changed = true; } } - if (!connector_state->crtc) { + if (!new_connector_state->crtc) { DRM_DEBUG_ATOMIC("Disabling [CONNECTOR:%d:%s]\n", connector->base.id, connector->name); - set_best_encoder(state, connector_state, NULL); + set_best_encoder(state, new_connector_state, NULL); return 0; } @@ -308,7 +309,7 @@ update_connector_routing(struct drm_atomic_state *state, if (funcs->atomic_best_encoder) new_encoder = funcs->atomic_best_encoder(connector, - connector_state); + new_connector_state); else if (funcs->best_encoder) new_encoder = funcs->best_encoder(connector); else @@ -321,34 +322,34 @@ update_connector_routing(struct drm_atomic_state *state, return -EINVAL; } - if (!drm_encoder_crtc_ok(new_encoder, connector_state->crtc)) { + if (!drm_encoder_crtc_ok(new_encoder, new_connector_state->crtc)) { DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] incompatible with [CRTC:%d:%s]\n", new_encoder->base.id, new_encoder->name, - connector_state->crtc->base.id, - connector_state->crtc->name); + new_connector_state->crtc->base.id, + new_connector_state->crtc->name); return -EINVAL; } - if (new_encoder == connector_state->best_encoder) { - set_best_encoder(state, connector_state, new_encoder); + if (new_encoder == new_connector_state->best_encoder) { + set_best_encoder(state, new_connector_state, new_encoder); DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d:%s]\n", connector->base.id, connector->name, new_encoder->base.id, new_encoder->name, - connector_state->crtc->base.id, - connector_state->crtc->name); + new_connector_state->crtc->base.id, + new_connector_state->crtc->name); return 0; } steal_encoder(state, new_encoder); - set_best_encoder(state, connector_state, new_encoder); + set_best_encoder(state, new_connector_state, new_encoder); - crtc_state = drm_atomic_get_existing_crtc_state(state, connector_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, new_connector_state->crtc); crtc_state->connectors_changed = true; DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d:%s]\n", @@ -356,8 +357,8 @@ update_connector_routing(struct drm_atomic_state *state, connector->name, new_encoder->base.id, new_encoder->name, - connector_state->crtc->base.id, - connector_state->crtc->name); + new_connector_state->crtc->base.id, + new_connector_state->crtc->name); return 0; } @@ -366,57 +367,57 @@ static int mode_fixup(struct drm_atomic_state *state) { struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_connector *connector; - struct drm_connector_state *conn_state; + struct drm_connector_state *new_conn_state; int i; int ret; - for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (!crtc_state->mode_changed && - !crtc_state->connectors_changed) + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + if (!new_crtc_state->mode_changed && + !new_crtc_state->connectors_changed) continue; - drm_mode_copy(&crtc_state->adjusted_mode, &crtc_state->mode); + drm_mode_copy(&new_crtc_state->adjusted_mode, &new_crtc_state->mode); } - for_each_connector_in_state(state, connector, conn_state, i) { + for_each_new_connector_in_state(state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; - WARN_ON(!!conn_state->best_encoder != !!conn_state->crtc); + WARN_ON(!!new_conn_state->best_encoder != !!new_conn_state->crtc); - if (!conn_state->crtc || !conn_state->best_encoder) + if (!new_conn_state->crtc || !new_conn_state->best_encoder) continue; - crtc_state = drm_atomic_get_existing_crtc_state(state, - conn_state->crtc); + new_crtc_state = + drm_atomic_get_new_crtc_state(state, new_conn_state->crtc); /* * Each encoder has at most one connector (since we always steal * it away), so we won't call ->mode_fixup twice. */ - encoder = conn_state->best_encoder; + encoder = new_conn_state->best_encoder; funcs = encoder->helper_private; - ret = drm_bridge_mode_fixup(encoder->bridge, &crtc_state->mode, - &crtc_state->adjusted_mode); + ret = drm_bridge_mode_fixup(encoder->bridge, &new_crtc_state->mode, + &new_crtc_state->adjusted_mode); if (!ret) { DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); return -EINVAL; } if (funcs && funcs->atomic_check) { - ret = funcs->atomic_check(encoder, crtc_state, - conn_state); + ret = funcs->atomic_check(encoder, new_crtc_state, + new_conn_state); if (ret) { DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] check failed\n", encoder->base.id, encoder->name); return ret; } } else if (funcs && funcs->mode_fixup) { - ret = funcs->mode_fixup(encoder, &crtc_state->mode, - &crtc_state->adjusted_mode); + ret = funcs->mode_fixup(encoder, &new_crtc_state->mode, + &new_crtc_state->adjusted_mode); if (!ret) { DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] fixup failed\n", encoder->base.id, encoder->name); @@ -425,22 +426,22 @@ mode_fixup(struct drm_atomic_state *state) } } - for_each_crtc_in_state(state, crtc, crtc_state, i) { + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; - if (!crtc_state->enable) + if (!new_crtc_state->enable) continue; - if (!crtc_state->mode_changed && - !crtc_state->connectors_changed) + if (!new_crtc_state->mode_changed && + !new_crtc_state->connectors_changed) continue; funcs = crtc->helper_private; if (!funcs->mode_fixup) continue; - ret = funcs->mode_fixup(crtc, &crtc_state->mode, - &crtc_state->adjusted_mode); + ret = funcs->mode_fixup(crtc, &new_crtc_state->mode, + &new_crtc_state->adjusted_mode); if (!ret) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] fixup failed\n", crtc->base.id, crtc->name); @@ -487,19 +488,19 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_connector *connector; - struct drm_connector_state *connector_state; + struct drm_connector_state *old_connector_state, *new_connector_state; int i, ret; - for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (!drm_mode_equal(&crtc->state->mode, &crtc_state->mode)) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + if (!drm_mode_equal(&old_crtc_state->mode, &new_crtc_state->mode)) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] mode changed\n", crtc->base.id, crtc->name); - crtc_state->mode_changed = true; + new_crtc_state->mode_changed = true; } - if (crtc->state->enable != crtc_state->enable) { + if (old_crtc_state->enable != new_crtc_state->enable) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enable changed\n", crtc->base.id, crtc->name); @@ -511,8 +512,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, * The other way around is true as well. enable != 0 * iff connectors are attached and a mode is set. */ - crtc_state->mode_changed = true; - crtc_state->connectors_changed = true; + new_crtc_state->mode_changed = true; + new_crtc_state->connectors_changed = true; } } @@ -520,22 +521,23 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, if (ret) return ret; - for_each_connector_in_state(state, connector, connector_state, i) { + for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) { /* * This only sets crtc->connectors_changed for routing changes, * drivers must set crtc->connectors_changed themselves when * connector properties need to be updated. */ ret = update_connector_routing(state, connector, - connector_state); + old_connector_state, + new_connector_state); if (ret) return ret; - if (connector->state->crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(state, - connector->state->crtc); - if (connector->state->link_status != - connector_state->link_status) - crtc_state->connectors_changed = true; + if (old_connector_state->crtc) { + new_crtc_state = drm_atomic_get_new_crtc_state(state, + old_connector_state->crtc); + if (old_connector_state->link_status != + new_connector_state->link_status) + new_crtc_state->connectors_changed = true; } } @@ -545,28 +547,28 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, * configuration. This must be done before calling mode_fixup in case a * crtc only changed its mode but has the same set of connectors. */ - for_each_crtc_in_state(state, crtc, crtc_state, i) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { bool has_connectors = - !!crtc_state->connector_mask; + !!new_crtc_state->connector_mask; /* * We must set ->active_changed after walking connectors for * otherwise an update that only changes active would result in * a full modeset because update_connector_routing force that. */ - if (crtc->state->active != crtc_state->active) { + if (old_crtc_state->active != new_crtc_state->active) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] active changed\n", crtc->base.id, crtc->name); - crtc_state->active_changed = true; + new_crtc_state->active_changed = true; } - if (!drm_atomic_crtc_needs_modeset(crtc_state)) + if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) continue; DRM_DEBUG_ATOMIC("[CRTC:%d:%s] needs all connectors, enable: %c, active: %c\n", crtc->base.id, crtc->name, - crtc_state->enable ? 'y' : 'n', - crtc_state->active ? 'y' : 'n'); + new_crtc_state->enable ? 'y' : 'n', + new_crtc_state->active ? 'y' : 'n'); ret = drm_atomic_add_affected_connectors(state, crtc); if (ret != 0) @@ -576,7 +578,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, if (ret != 0) return ret; - if (crtc_state->enable != has_connectors) { + if (new_crtc_state->enable != has_connectors) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enabled/connectors mismatch\n", crtc->base.id, crtc->name); @@ -609,22 +611,22 @@ drm_atomic_helper_check_planes(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_plane *plane; - struct drm_plane_state *plane_state; + struct drm_plane_state *new_plane_state, *old_plane_state; int i, ret = 0; - for_each_plane_in_state(state, plane, plane_state, i) { + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { const struct drm_plane_helper_funcs *funcs; funcs = plane->helper_private; - drm_atomic_helper_plane_changed(state, plane_state, plane); + drm_atomic_helper_plane_changed(state, old_plane_state, new_plane_state, plane); if (!funcs || !funcs->atomic_check) continue; - ret = funcs->atomic_check(plane, plane_state); + ret = funcs->atomic_check(plane, new_plane_state); if (ret) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] atomic driver check failed\n", plane->base.id, plane->name); @@ -632,7 +634,7 @@ drm_atomic_helper_check_planes(struct drm_device *dev, } } - for_each_crtc_in_state(state, crtc, crtc_state, i) { + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; funcs = crtc->helper_private; @@ -640,7 +642,7 @@ drm_atomic_helper_check_planes(struct drm_device *dev, if (!funcs || !funcs->atomic_check) continue; - ret = funcs->atomic_check(crtc, crtc_state); + ret = funcs->atomic_check(crtc, new_crtc_state); if (ret) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] atomic driver check failed\n", crtc->base.id, crtc->name); @@ -694,12 +696,12 @@ static void disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) { struct drm_connector *connector; - struct drm_connector_state *old_conn_state; + struct drm_connector_state *old_conn_state, *new_conn_state; struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; int i; - for_each_connector_in_state(old_state, connector, old_conn_state, i) { + for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; @@ -708,8 +710,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) if (!old_conn_state->crtc) continue; - old_crtc_state = drm_atomic_get_existing_crtc_state(old_state, - old_conn_state->crtc); + old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc); if (!old_crtc_state->active || !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state)) @@ -736,7 +737,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) /* Right function depends upon target state. */ if (funcs) { - if (connector->state->crtc && funcs->prepare) + if (new_conn_state->crtc && funcs->prepare) funcs->prepare(encoder); else if (funcs->disable) funcs->disable(encoder); @@ -747,11 +748,11 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) drm_bridge_post_disable(encoder->bridge); } - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { + for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; /* Shut down everything that needs a full modeset. */ - if (!drm_atomic_crtc_needs_modeset(crtc->state)) + if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) continue; if (!old_crtc_state->active) @@ -764,7 +765,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) /* Right function depends upon target state. */ - if (crtc->state->enable && funcs->prepare) + if (new_crtc_state->enable && funcs->prepare) funcs->prepare(crtc); else if (funcs->atomic_disable) funcs->atomic_disable(crtc, old_crtc_state); @@ -793,13 +794,13 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, struct drm_atomic_state *old_state) { struct drm_connector *connector; - struct drm_connector_state *old_conn_state; + struct drm_connector_state *old_conn_state, *new_conn_state; struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; + struct drm_crtc_state *new_crtc_state; int i; /* clear out existing links and update dpms */ - for_each_connector_in_state(old_state, connector, old_conn_state, i) { + for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) { if (connector->encoder) { WARN_ON(!connector->encoder->crtc); @@ -807,7 +808,7 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, connector->encoder = NULL; } - crtc = connector->state->crtc; + crtc = new_conn_state->crtc; if ((!crtc && old_conn_state->crtc) || (crtc && drm_atomic_crtc_needs_modeset(crtc->state))) { struct drm_property *dpms_prop = @@ -824,33 +825,36 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, } /* set new links */ - for_each_connector_in_state(old_state, connector, old_conn_state, i) { - if (!connector->state->crtc) + for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { + if (!new_conn_state->crtc) continue; - if (WARN_ON(!connector->state->best_encoder)) + if (WARN_ON(!new_conn_state->best_encoder)) continue; - connector->encoder = connector->state->best_encoder; - connector->encoder->crtc = connector->state->crtc; + connector->encoder = new_conn_state->best_encoder; + connector->encoder->crtc = new_conn_state->crtc; } /* set legacy state in the crtc structure */ - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { struct drm_plane *primary = crtc->primary; + struct drm_plane_state *new_plane_state; - crtc->mode = crtc->state->mode; - crtc->enabled = crtc->state->enable; + crtc->mode = new_crtc_state->mode; + crtc->enabled = new_crtc_state->enable; - if (drm_atomic_get_existing_plane_state(old_state, primary) && - primary->state->crtc == crtc) { - crtc->x = primary->state->src_x >> 16; - crtc->y = primary->state->src_y >> 16; + new_plane_state = + drm_atomic_get_new_plane_state(old_state, primary); + + if (new_plane_state && new_plane_state->crtc == crtc) { + crtc->x = new_plane_state->src_x >> 16; + crtc->y = new_plane_state->src_y >> 16; } - if (crtc->state->enable) + if (new_crtc_state->enable) drm_calc_timestamping_constants(crtc, - &crtc->state->adjusted_mode); + &new_crtc_state->adjusted_mode); } } EXPORT_SYMBOL(drm_atomic_helper_update_legacy_modeset_state); @@ -859,20 +863,20 @@ static void crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) { struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_connector *connector; - struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; int i; - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; - if (!crtc->state->mode_changed) + if (!new_crtc_state->mode_changed) continue; funcs = crtc->helper_private; - if (crtc->state->enable && funcs->mode_set_nofb) { + if (new_crtc_state->enable && funcs->mode_set_nofb) { DRM_DEBUG_ATOMIC("modeset on [CRTC:%d:%s]\n", crtc->base.id, crtc->name); @@ -880,18 +884,17 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) } } - for_each_connector_in_state(old_state, connector, old_conn_state, i) { + for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; - struct drm_crtc_state *new_crtc_state; struct drm_encoder *encoder; struct drm_display_mode *mode, *adjusted_mode; - if (!connector->state->best_encoder) + if (!new_conn_state->best_encoder) continue; - encoder = connector->state->best_encoder; + encoder = new_conn_state->best_encoder; funcs = encoder->helper_private; - new_crtc_state = connector->state->crtc->state; + new_crtc_state = new_conn_state->crtc->state; mode = &new_crtc_state->mode; adjusted_mode = &new_crtc_state->adjusted_mode; @@ -907,7 +910,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) */ if (funcs && funcs->atomic_mode_set) { funcs->atomic_mode_set(encoder, new_crtc_state, - connector->state); + new_conn_state); } else if (funcs && funcs->mode_set) { funcs->mode_set(encoder, mode, adjusted_mode); } @@ -959,24 +962,24 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *old_state) { struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_connector *connector; - struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; int i; - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; /* Need to filter out CRTCs where only planes change. */ - if (!drm_atomic_crtc_needs_modeset(crtc->state)) + if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) continue; - if (!crtc->state->active) + if (!new_crtc_state->active) continue; funcs = crtc->helper_private; - if (crtc->state->enable) { + if (new_crtc_state->enable) { DRM_DEBUG_ATOMIC("enabling [CRTC:%d:%s]\n", crtc->base.id, crtc->name); @@ -987,18 +990,18 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, } } - for_each_connector_in_state(old_state, connector, old_conn_state, i) { + for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; - if (!connector->state->best_encoder) + if (!new_conn_state->best_encoder) continue; - if (!connector->state->crtc->state->active || - !drm_atomic_crtc_needs_modeset(connector->state->crtc->state)) + if (!new_conn_state->crtc->state->active || + !drm_atomic_crtc_needs_modeset(new_conn_state->crtc->state)) continue; - encoder = connector->state->best_encoder; + encoder = new_conn_state->best_encoder; funcs = encoder->helper_private; DRM_DEBUG_ATOMIC("enabling [ENCODER:%d:%s]\n", @@ -1048,29 +1051,26 @@ int drm_atomic_helper_wait_for_fences(struct drm_device *dev, bool pre_swap) { struct drm_plane *plane; - struct drm_plane_state *plane_state; + struct drm_plane_state *new_plane_state; int i, ret; - for_each_plane_in_state(state, plane, plane_state, i) { - if (!pre_swap) - plane_state = plane->state; - - if (!plane_state->fence) + for_each_new_plane_in_state(state, plane, new_plane_state, i) { + if (!new_plane_state->fence) continue; - WARN_ON(!plane_state->fb); + WARN_ON(!new_plane_state->fb); /* * If waiting for fences pre-swap (ie: nonblock), userspace can * still interrupt the operation. Instead of blocking until the * timer expires, make the wait interruptible. */ - ret = dma_fence_wait(plane_state->fence, pre_swap); + ret = dma_fence_wait(new_plane_state->fence, pre_swap); if (ret) return ret; - dma_fence_put(plane_state->fence); - plane_state->fence = NULL; + dma_fence_put(new_plane_state->fence); + new_plane_state->fence = NULL; } return 0; @@ -1093,7 +1093,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, struct drm_atomic_state *old_state) { struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; int i, ret; unsigned crtc_mask = 0; @@ -1104,9 +1104,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, if (old_state->legacy_cursor_update) return; - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - struct drm_crtc_state *new_crtc_state = crtc->state; - + for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { if (!new_crtc_state->active || !new_crtc_state->planes_changed) continue; @@ -1118,7 +1116,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, old_state->crtcs[i].last_vblank_count = drm_crtc_vblank_count(crtc); } - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { + for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) { if (!(crtc_mask & drm_crtc_mask(crtc))) continue; @@ -1427,11 +1425,11 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, bool nonblock) { struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_crtc_commit *commit; int i, ret; - for_each_crtc_in_state(state, crtc, crtc_state, i) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { commit = kzalloc(sizeof(*commit), GFP_KERNEL); if (!commit) return -ENOMEM; @@ -1452,7 +1450,7 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, /* Drivers only send out events when at least either current or * new CRTC state is active. Complete right away if everything * stays off. */ - if (!crtc->state->active && !crtc_state->active) { + if (!old_crtc_state->active && !new_crtc_state->active) { complete_all(&commit->flip_done); continue; } @@ -1463,17 +1461,17 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, continue; } - if (!crtc_state->event) { + if (!new_crtc_state->event) { commit->event = kzalloc(sizeof(*commit->event), GFP_KERNEL); if (!commit->event) return -ENOMEM; - crtc_state->event = commit->event; + new_crtc_state->event = commit->event; } - crtc_state->event->base.completion = &commit->flip_done; - crtc_state->event->base.completion_release = release_crtc_commit; + new_crtc_state->event->base.completion = &commit->flip_done; + new_crtc_state->event->base.completion_release = release_crtc_commit; drm_crtc_commit_get(commit); } @@ -1512,12 +1510,12 @@ static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc) void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state) { struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_crtc_commit *commit; int i; long ret; - for_each_crtc_in_state(old_state, crtc, crtc_state, i) { + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { spin_lock(&crtc->commit_lock); commit = preceeding_commit(crtc); if (commit) @@ -1564,17 +1562,17 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state) { struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_crtc_commit *commit; int i; - for_each_crtc_in_state(old_state, crtc, crtc_state, i) { + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { commit = old_state->crtcs[i].commit; if (!commit) continue; /* backend must have consumed any event by now */ - WARN_ON(crtc->state->event); + WARN_ON(new_crtc_state->event); spin_lock(&crtc->commit_lock); complete_all(&commit->hw_done); spin_unlock(&crtc->commit_lock); @@ -1596,12 +1594,12 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done); void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state) { struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_crtc_commit *commit; int i; long ret; - for_each_crtc_in_state(old_state, crtc, crtc_state, i) { + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { commit = old_state->crtcs[i].commit; if (WARN_ON(!commit)) continue; @@ -1652,16 +1650,16 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_plane *plane; - struct drm_plane_state *plane_state; + struct drm_plane_state *new_plane_state; int ret, i, j; - for_each_plane_in_state(state, plane, plane_state, i) { + for_each_new_plane_in_state(state, plane, new_plane_state, i) { const struct drm_plane_helper_funcs *funcs; funcs = plane->helper_private; if (funcs->prepare_fb) { - ret = funcs->prepare_fb(plane, plane_state); + ret = funcs->prepare_fb(plane, new_plane_state); if (ret) goto fail; } @@ -1670,7 +1668,7 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev, return 0; fail: - for_each_plane_in_state(state, plane, plane_state, j) { + for_each_new_plane_in_state(state, plane, new_plane_state, j) { const struct drm_plane_helper_funcs *funcs; if (j >= i) @@ -1679,7 +1677,7 @@ fail: funcs = plane->helper_private; if (funcs->cleanup_fb) - funcs->cleanup_fb(plane, plane_state); + funcs->cleanup_fb(plane, new_plane_state); } return ret; @@ -1737,14 +1735,14 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, uint32_t flags) { struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_plane *plane; - struct drm_plane_state *old_plane_state; + struct drm_plane_state *old_plane_state, *new_plane_state; int i; bool active_only = flags & DRM_PLANE_COMMIT_ACTIVE_ONLY; bool no_disable = flags & DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET; - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { + for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; funcs = crtc->helper_private; @@ -1752,13 +1750,13 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, if (!funcs || !funcs->atomic_begin) continue; - if (active_only && !crtc->state->active) + if (active_only && !new_crtc_state->active) continue; funcs->atomic_begin(crtc, old_crtc_state); } - for_each_plane_in_state(old_state, plane, old_plane_state, i) { + for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) { const struct drm_plane_helper_funcs *funcs; bool disabling; @@ -1767,7 +1765,8 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, if (!funcs) continue; - disabling = drm_atomic_plane_disabling(plane, old_plane_state); + disabling = drm_atomic_plane_disabling(old_plane_state, + new_plane_state); if (active_only) { /* @@ -1777,7 +1776,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, * CRTC to avoid skipping planes being disabled on an * active CRTC. */ - if (!disabling && !plane_crtc_active(plane->state)) + if (!disabling && !plane_crtc_active(new_plane_state)) continue; if (disabling && !plane_crtc_active(old_plane_state)) continue; @@ -1796,12 +1795,12 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, continue; funcs->atomic_disable(plane, old_plane_state); - } else if (plane->state->crtc || disabling) { + } else if (new_plane_state->crtc || disabling) { funcs->atomic_update(plane, old_plane_state); } } - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { + for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; funcs = crtc->helper_private; @@ -1809,7 +1808,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, if (!funcs || !funcs->atomic_flush) continue; - if (active_only && !crtc->state->active) + if (active_only && !new_crtc_state->active) continue; funcs->atomic_flush(crtc, old_crtc_state); @@ -1852,7 +1851,7 @@ drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state) drm_for_each_plane_mask(plane, crtc->dev, plane_mask) { struct drm_plane_state *old_plane_state = - drm_atomic_get_existing_plane_state(old_state, plane); + drm_atomic_get_old_plane_state(old_state, plane); const struct drm_plane_helper_funcs *plane_funcs; plane_funcs = plane->helper_private; @@ -1862,11 +1861,11 @@ drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state) WARN_ON(plane->state->crtc && plane->state->crtc != crtc); - if (drm_atomic_plane_disabling(plane, old_plane_state) && + if (drm_atomic_plane_disabling(old_plane_state, plane->state) && plane_funcs->atomic_disable) plane_funcs->atomic_disable(plane, old_plane_state); else if (plane->state->crtc || - drm_atomic_plane_disabling(plane, old_plane_state)) + drm_atomic_plane_disabling(old_plane_state, plane->state)) plane_funcs->atomic_update(plane, old_plane_state); } @@ -1936,11 +1935,21 @@ void drm_atomic_helper_cleanup_planes(struct drm_device *dev, struct drm_atomic_state *old_state) { struct drm_plane *plane; - struct drm_plane_state *plane_state; + struct drm_plane_state *old_plane_state, *new_plane_state; int i; - for_each_plane_in_state(old_state, plane, plane_state, i) { + for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) { const struct drm_plane_helper_funcs *funcs; + struct drm_plane_state *plane_state; + + /* + * This might be called before swapping when commit is aborted, + * in which case we have to cleanup the new state. + */ + if (old_plane_state == plane->state) + plane_state = new_plane_state; + else + plane_state = old_plane_state; funcs = plane->helper_private; @@ -1986,15 +1995,15 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state, int i; long ret; struct drm_connector *connector; - struct drm_connector_state *conn_state, *old_conn_state; + struct drm_connector_state *old_conn_state, *new_conn_state; struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state, *old_crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_plane *plane; - struct drm_plane_state *plane_state, *old_plane_state; + struct drm_plane_state *old_plane_state, *new_plane_state; struct drm_crtc_commit *commit; if (stall) { - for_each_crtc_in_state(state, crtc, crtc_state, i) { + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { spin_lock(&crtc->commit_lock); commit = list_first_entry_or_null(&crtc->commit_list, struct drm_crtc_commit, commit_entry); @@ -2014,20 +2023,24 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state, } } - for_each_oldnew_connector_in_state(state, connector, old_conn_state, conn_state, i) { + for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) { WARN_ON(connector->state != old_conn_state); - connector->state->state = state; - swap(state->connectors[i].state, connector->state); - connector->state->state = NULL; + old_conn_state->state = state; + new_conn_state->state = NULL; + + state->connectors[i].state = old_conn_state; + connector->state = new_conn_state; } - for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, crtc_state, i) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { WARN_ON(crtc->state != old_crtc_state); - crtc->state->state = state; - swap(state->crtcs[i].state, crtc->state); - crtc->state->state = NULL; + old_crtc_state->state = state; + new_crtc_state->state = NULL; + + state->crtcs[i].state = old_crtc_state; + crtc->state = new_crtc_state; if (state->crtcs[i].commit) { spin_lock(&crtc->commit_lock); @@ -2039,12 +2052,14 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state, } } - for_each_oldnew_plane_in_state(state, plane, old_plane_state, plane_state, i) { + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { WARN_ON(plane->state != old_plane_state); - plane->state->state = state; - swap(state->planes[i].state, plane->state); - plane->state->state = NULL; + old_plane_state->state = state; + new_plane_state->state = NULL; + + state->planes[i].state = old_plane_state; + plane->state = new_plane_state; } } EXPORT_SYMBOL(drm_atomic_helper_swap_state); @@ -2227,9 +2242,9 @@ static int update_output_state(struct drm_atomic_state *state, { struct drm_device *dev = set->crtc->dev; struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_connector *connector; - struct drm_connector_state *conn_state; + struct drm_connector_state *new_conn_state; int ret, i; ret = drm_modeset_lock(&dev->mode_config.connection_mutex, @@ -2242,31 +2257,32 @@ static int update_output_state(struct drm_atomic_state *state, if (ret) return ret; - for_each_connector_in_state(state, connector, conn_state, i) { - if (conn_state->crtc == set->crtc) { - ret = drm_atomic_set_crtc_for_connector(conn_state, + for_each_new_connector_in_state(state, connector, new_conn_state, i) { + if (new_conn_state->crtc == set->crtc) { + ret = drm_atomic_set_crtc_for_connector(new_conn_state, NULL); if (ret) return ret; + /* Make sure legacy setCrtc always re-trains */ - conn_state->link_status = DRM_LINK_STATUS_GOOD; + new_conn_state->link_status = DRM_LINK_STATUS_GOOD; } } /* Then set all connectors from set->connectors on the target crtc */ for (i = 0; i < set->num_connectors; i++) { - conn_state = drm_atomic_get_connector_state(state, + new_conn_state = drm_atomic_get_connector_state(state, set->connectors[i]); - if (IS_ERR(conn_state)) - return PTR_ERR(conn_state); + if (IS_ERR(new_conn_state)) + return PTR_ERR(new_conn_state); - ret = drm_atomic_set_crtc_for_connector(conn_state, + ret = drm_atomic_set_crtc_for_connector(new_conn_state, set->crtc); if (ret) return ret; } - for_each_crtc_in_state(state, crtc, crtc_state, i) { + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { /* Don't update ->enable for the CRTC in the set_config request, * since a mismatch would indicate a bug in the upper layers. * The actual modeset code later on will catch any @@ -2274,13 +2290,13 @@ static int update_output_state(struct drm_atomic_state *state, if (crtc == set->crtc) continue; - if (!crtc_state->connector_mask) { - ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, + if (!new_crtc_state->connector_mask) { + ret = drm_atomic_set_mode_prop_for_crtc(new_crtc_state, NULL); if (ret < 0) return ret; - crtc_state->active = false; + new_crtc_state->active = false; } } @@ -2583,21 +2599,21 @@ int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state, { int i; struct drm_plane *plane; - struct drm_plane_state *plane_state; + struct drm_plane_state *new_plane_state; struct drm_connector *connector; - struct drm_connector_state *conn_state; + struct drm_connector_state *new_conn_state; struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *new_crtc_state; state->acquire_ctx = ctx; - for_each_new_plane_in_state(state, plane, plane_state, i) + for_each_new_plane_in_state(state, plane, new_plane_state, i) state->planes[i].old_state = plane->state; - for_each_new_crtc_in_state(state, crtc, crtc_state, i) + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) state->crtcs[i].old_state = crtc->state; - for_each_new_connector_in_state(state, connector, conn_state, i) + for_each_new_connector_in_state(state, connector, new_conn_state, i) state->connectors[i].old_state = connector->state; return drm_atomic_commit(state); @@ -2938,7 +2954,7 @@ retry: if (ret != 0) goto fail; - crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (WARN_ON(!crtc_state)) { ret = -EINVAL; goto fail; diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c index 665aafc6ad68..a0d0d6843288 100644 --- a/drivers/gpu/drm/drm_blend.c +++ b/drivers/gpu/drm/drm_blend.c @@ -377,27 +377,26 @@ int drm_atomic_normalize_zpos(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_plane *plane; - struct drm_plane_state *plane_state; + struct drm_plane_state *old_plane_state, *new_plane_state; int i, ret = 0; - for_each_plane_in_state(state, plane, plane_state, i) { - crtc = plane_state->crtc; + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { + crtc = new_plane_state->crtc; if (!crtc) continue; - if (plane->state->zpos != plane_state->zpos) { - crtc_state = - drm_atomic_get_existing_crtc_state(state, crtc); - crtc_state->zpos_changed = true; + if (old_plane_state->zpos != new_plane_state->zpos) { + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + new_crtc_state->zpos_changed = true; } } - for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (crtc_state->plane_mask != crtc->state->plane_mask || - crtc_state->zpos_changed) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + if (old_crtc_state->plane_mask != new_crtc_state->plane_mask || + new_crtc_state->zpos_changed) { ret = drm_atomic_helper_crtc_normalize_zpos(crtc, - crtc_state); + new_crtc_state); if (ret) return ret; } diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 8b2c61ae0004..1d2d18d82d2e 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -242,14 +242,9 @@ static void drm_debugfs_remove_all_files(struct drm_minor *minor) */ int drm_debugfs_cleanup(struct drm_minor *minor) { - struct drm_device *dev = minor->dev; - if (!minor->debugfs_root) return 0; - if (dev->driver->debugfs_cleanup) - dev->driver->debugfs_cleanup(minor); - drm_debugfs_remove_all_files(minor); debugfs_remove_recursive(minor->debugfs_root); diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 68908c1d5ca1..3e5f52110ea1 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -981,6 +981,78 @@ static const struct i2c_lock_operations drm_dp_i2c_lock_ops = { .unlock_bus = unlock_bus, }; +static int drm_dp_aux_get_crc(struct drm_dp_aux *aux, u8 *crc) +{ + u8 buf, count; + int ret; + + ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf); + if (ret < 0) + return ret; + + WARN_ON(!(buf & DP_TEST_SINK_START)); + + ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK_MISC, &buf); + if (ret < 0) + return ret; + + count = buf & DP_TEST_COUNT_MASK; + if (count == aux->crc_count) + return -EAGAIN; /* No CRC yet */ + + aux->crc_count = count; + + /* + * At DP_TEST_CRC_R_CR, there's 6 bytes containing CRC data, 2 bytes + * per component (RGB or CrYCb). + */ + ret = drm_dp_dpcd_read(aux, DP_TEST_CRC_R_CR, crc, 6); + if (ret < 0) + return ret; + + return 0; +} + +static void drm_dp_aux_crc_work(struct work_struct *work) +{ + struct drm_dp_aux *aux = container_of(work, struct drm_dp_aux, + crc_work); + struct drm_crtc *crtc; + u8 crc_bytes[6]; + uint32_t crcs[3]; + int ret; + + if (WARN_ON(!aux->crtc)) + return; + + crtc = aux->crtc; + while (crtc->crc.opened) { + drm_crtc_wait_one_vblank(crtc); + if (!crtc->crc.opened) + break; + + ret = drm_dp_aux_get_crc(aux, crc_bytes); + if (ret == -EAGAIN) { + usleep_range(1000, 2000); + ret = drm_dp_aux_get_crc(aux, crc_bytes); + } + + if (ret == -EAGAIN) { + DRM_DEBUG_KMS("Get CRC failed after retrying: %d\n", + ret); + continue; + } else if (ret) { + DRM_DEBUG_KMS("Failed to get a CRC: %d\n", ret); + continue; + } + + crcs[0] = crc_bytes[0] | crc_bytes[1] << 8; + crcs[1] = crc_bytes[2] | crc_bytes[3] << 8; + crcs[2] = crc_bytes[4] | crc_bytes[5] << 8; + drm_crtc_add_crc_entry(crtc, false, 0, crcs); + } +} + /** * drm_dp_aux_init() - minimally initialise an aux channel * @aux: DisplayPort AUX channel @@ -993,6 +1065,7 @@ static const struct i2c_lock_operations drm_dp_i2c_lock_ops = { void drm_dp_aux_init(struct drm_dp_aux *aux) { mutex_init(&aux->hw_mutex); + INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work); aux->ddc.algo = &drm_dp_i2c_algo; aux->ddc.algo_data = aux; @@ -1081,3 +1154,57 @@ int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE]) EXPORT_SYMBOL(drm_dp_psr_setup_time); #undef PSR_SETUP_TIME + +/** + * drm_dp_start_crc() - start capture of frame CRCs + * @aux: DisplayPort AUX channel + * @crtc: CRTC displaying the frames whose CRCs are to be captured + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc) +{ + u8 buf; + int ret; + + ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf | DP_TEST_SINK_START); + if (ret < 0) + return ret; + + aux->crc_count = 0; + aux->crtc = crtc; + schedule_work(&aux->crc_work); + + return 0; +} +EXPORT_SYMBOL(drm_dp_start_crc); + +/** + * drm_dp_stop_crc() - stop capture of frame CRCs + * @aux: DisplayPort AUX channel + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_stop_crc(struct drm_dp_aux *aux) +{ + u8 buf; + int ret; + + ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf & ~DP_TEST_SINK_START); + if (ret < 0) + return ret; + + flush_work(&aux->crc_work); + aux->crtc = NULL; + + return 0; +} +EXPORT_SYMBOL(drm_dp_stop_crc); diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_file.c index afdf5b147f39..d9e63d73d3ec 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_file.c @@ -1,7 +1,4 @@ /* - * \file drm_fops.c - * File operations for DRM - * * \author Rickard E. (Rik) Faith <faith@valinux.com> * \author Daryll Strauss <daryll@valinux.com> * \author Gareth Hughes <gareth@valinux.com> @@ -34,10 +31,13 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include <drm/drmP.h> #include <linux/poll.h> #include <linux/slab.h> #include <linux/module.h> + +#include <drm/drm_file.h> +#include <drm/drmP.h> + #include "drm_legacy.h" #include "drm_internal.h" #include "drm_crtc_internal.h" diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 90d2cc8da8eb..92bf3306d4b3 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -132,6 +132,8 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_RGBX8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_BGRX8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_RGB565_A8, .depth = 24, .num_planes = 2, .cpp = { 2, 1, 0 }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_BGR565_A8, .depth = 24, .num_planes = 2, .cpp = { 2, 1, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_RGBX1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, @@ -144,6 +146,12 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_RGBA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_RGB888_A8, .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_BGR888_A8, .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_XRGB8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_XBGR8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_RGBX8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_BGRX8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_YUV410, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 4 }, { .format = DRM_FORMAT_YVU410, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 4 }, { .format = DRM_FORMAT_YUV411, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 1 }, diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index f37388cb2fde..92ff4b9393b1 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -24,7 +24,7 @@ #define DRM_IF_MAJOR 1 #define DRM_IF_MINOR 4 -/* drm_fops.c */ +/* drm_file.c */ extern struct mutex drm_global_mutex; void drm_lastclose(struct drm_device *dev); diff --git a/drivers/gpu/drm/drm_kms_helper_common.c b/drivers/gpu/drm/drm_kms_helper_common.c index 45db36cd3d20..6e35a56a6102 100644 --- a/drivers/gpu/drm/drm_kms_helper_common.c +++ b/drivers/gpu/drm/drm_kms_helper_common.c @@ -25,8 +25,7 @@ * */ -#include <drm/drmP.h> -#include <drm/drm_fb_helper.h> +#include <linux/module.h> #include "drm_crtc_helper_internal.h" diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index a3b356e70b35..1eb4fc3eee20 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include <linux/dma-mapping.h> #include <linux/export.h> +#include <drm/drm_pci.h> #include <drm/drmP.h> #include "drm_internal.h" #include "drm_legacy.h" @@ -36,6 +37,9 @@ * @size: size of block to allocate * @align: alignment of block * + * FIXME: This is a needless abstraction of the Linux dma-api and should be + * removed. + * * Return: A handle to the allocated memory block on success or NULL on * failure. */ @@ -104,6 +108,9 @@ void __drm_legacy_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) * drm_pci_free - Free a PCI consistent memory block * @dev: DRM device * @dmah: handle to memory block + * + * FIXME: This is a needless abstraction of the Linux dma-api and should be + * removed. */ void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) { diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 244cf2619294..de1ac5e08f4d 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -469,7 +469,7 @@ int drm_plane_helper_commit(struct drm_plane *plane, * Drivers may optionally implement the ->atomic_disable callback, so * special-case that here. */ - if (drm_atomic_plane_disabling(plane, plane_state) && + if (drm_atomic_plane_disabling(plane_state, plane->state) && plane_funcs->atomic_disable) plane_funcs->atomic_disable(plane, plane_state); else diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c deleted file mode 100644 index 56d2f93ed6b9..000000000000 --- a/drivers/gpu/drm/drm_platform.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Derived from drm_pci.c - * - * Copyright 2003 José Fonseca. - * Copyright 2003 Leif Delgass. - * Copyright (c) 2009, Code Aurora Forum. - * All Rights Reserved. - * - * 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 (including the next - * paragraph) 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 - * AUTHORS 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 <linux/export.h> -#include <drm/drmP.h> - -/* - * Register. - * - * \param platdev - Platform device struture - * \return zero on success or a negative number on failure. - * - * Attempt to gets inter module "drm" information. If we are first - * then register the character device and inter module information. - * Try and register, if we fail to register, backout previous work. - */ - -static int drm_get_platform_dev(struct platform_device *platdev, - struct drm_driver *driver) -{ - struct drm_device *dev; - int ret; - - DRM_DEBUG("\n"); - - dev = drm_dev_alloc(driver, &platdev->dev); - if (IS_ERR(dev)) - return PTR_ERR(dev); - - dev->platformdev = platdev; - - ret = drm_dev_register(dev, 0); - if (ret) - goto err_free; - - return 0; - -err_free: - drm_dev_unref(dev); - return ret; -} - -/** - * drm_platform_init - Register a platform device with the DRM subsystem - * @driver: DRM device driver - * @platform_device: platform device to register - * - * Registers the specified DRM device driver and platform device with the DRM - * subsystem, initializing a drm_device structure and calling the driver's - * .load() function. - * - * NOTE: This function is deprecated, please use drm_dev_alloc() and - * drm_dev_register() instead and remove your &drm_driver.load callback. - * - * Return: 0 on success or a negative error code on failure. - */ -int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device) -{ - DRM_DEBUG("\n"); - - return drm_get_platform_dev(platform_device, driver); -} -EXPORT_SYMBOL(drm_platform_init); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 866b294e7c61..9fb65b736a90 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -29,8 +29,9 @@ #include <linux/export.h> #include <linux/dma-buf.h> #include <linux/rbtree.h> -#include <drm/drmP.h> +#include <drm/drm_prime.h> #include <drm/drm_gem.h> +#include <drm/drmP.h> #include "drm_internal.h" diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 35c5d99296b9..16789faa9291 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -86,8 +86,8 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, int ret; pipe = container_of(plane, struct drm_simple_display_pipe, plane); - crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state, - &pipe->crtc); + crtc_state = drm_atomic_get_new_crtc_state(plane_state->state, + &pipe->crtc); if (crtc_state->enable != !!plane_state->crtc) return -EINVAL; /* plane must match crtc enable state */ diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index 1ef0be338b85..b445b50a5dc4 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -101,7 +101,6 @@ static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data, struct exynos_dp_device *dp = to_dp(plat_data); int ret; - drm_connector_register(connector); dp->connector = connector; /* Pre-empt DP connector creation if there's a bridge */ diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index ad6b73c7fc59..3aab71a485ba 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -114,7 +114,6 @@ static int exynos_dpi_create_connector(struct drm_encoder *encoder) } drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs); - drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index b4522f67b3cb..09d3c4c3c858 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -39,118 +39,6 @@ static struct device *exynos_drm_get_dma_device(void); -static int exynos_drm_load(struct drm_device *dev, unsigned long flags) -{ - struct exynos_drm_private *private; - struct drm_encoder *encoder; - unsigned int clone_mask; - int cnt, ret; - - private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL); - if (!private) - return -ENOMEM; - - init_waitqueue_head(&private->wait); - spin_lock_init(&private->lock); - - dev_set_drvdata(dev->dev, dev); - dev->dev_private = (void *)private; - - /* the first real CRTC device is used for all dma mapping operations */ - private->dma_dev = exynos_drm_get_dma_device(); - if (!private->dma_dev) { - DRM_ERROR("no device found for DMA mapping operations.\n"); - ret = -ENODEV; - goto err_free_private; - } - DRM_INFO("Exynos DRM: using %s device for DMA mapping operations\n", - dev_name(private->dma_dev)); - - /* create common IOMMU mapping for all devices attached to Exynos DRM */ - ret = drm_create_iommu_mapping(dev); - if (ret < 0) { - DRM_ERROR("failed to create iommu mapping.\n"); - goto err_free_private; - } - - drm_mode_config_init(dev); - - exynos_drm_mode_config_init(dev); - - /* setup possible_clones. */ - cnt = 0; - clone_mask = 0; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) - clone_mask |= (1 << (cnt++)); - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) - encoder->possible_clones = clone_mask; - - platform_set_drvdata(dev->platformdev, dev); - - /* Try to bind all sub drivers. */ - ret = component_bind_all(dev->dev, dev); - if (ret) - goto err_mode_config_cleanup; - - ret = drm_vblank_init(dev, dev->mode_config.num_crtc); - if (ret) - goto err_unbind_all; - - /* Probe non kms sub drivers and virtual display driver. */ - ret = exynos_drm_device_subdrv_probe(dev); - if (ret) - goto err_cleanup_vblank; - - drm_mode_config_reset(dev); - - /* - * enable drm irq mode. - * - with irq_enabled = true, we can use the vblank feature. - * - * P.S. note that we wouldn't use drm irq handler but - * just specific driver own one instead because - * drm framework supports only one irq handler. - */ - dev->irq_enabled = true; - - /* init kms poll for handling hpd */ - drm_kms_helper_poll_init(dev); - - /* force connectors detection */ - drm_helper_hpd_irq_event(dev); - - return 0; - -err_cleanup_vblank: - drm_vblank_cleanup(dev); -err_unbind_all: - component_unbind_all(dev->dev, dev); -err_mode_config_cleanup: - drm_mode_config_cleanup(dev); - drm_release_iommu_mapping(dev); -err_free_private: - kfree(private); - - return ret; -} - -static void exynos_drm_unload(struct drm_device *dev) -{ - exynos_drm_device_subdrv_remove(dev); - - exynos_drm_fbdev_fini(dev); - drm_kms_helper_poll_fini(dev); - - drm_vblank_cleanup(dev); - component_unbind_all(dev->dev, dev); - drm_mode_config_cleanup(dev); - drm_release_iommu_mapping(dev); - - kfree(dev->dev_private); - dev->dev_private = NULL; -} - int exynos_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { @@ -256,8 +144,6 @@ static const struct file_operations exynos_drm_driver_fops = { static struct drm_driver exynos_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC | DRIVER_RENDER, - .load = exynos_drm_load, - .unload = exynos_drm_unload, .open = exynos_drm_open, .preclose = exynos_drm_preclose, .lastclose = exynos_drm_lastclose, @@ -432,12 +318,135 @@ static struct component_match *exynos_drm_match_add(struct device *dev) static int exynos_drm_bind(struct device *dev) { - return drm_platform_init(&exynos_drm_driver, to_platform_device(dev)); + struct exynos_drm_private *private; + struct drm_encoder *encoder; + struct drm_device *drm; + unsigned int clone_mask; + int cnt, ret; + + drm = drm_dev_alloc(&exynos_drm_driver, dev); + if (IS_ERR(drm)) + return PTR_ERR(drm); + + private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL); + if (!private) { + ret = -ENOMEM; + goto err_free_drm; + } + + init_waitqueue_head(&private->wait); + spin_lock_init(&private->lock); + + dev_set_drvdata(dev, drm); + drm->dev_private = (void *)private; + + /* the first real CRTC device is used for all dma mapping operations */ + private->dma_dev = exynos_drm_get_dma_device(); + if (!private->dma_dev) { + DRM_ERROR("no device found for DMA mapping operations.\n"); + ret = -ENODEV; + goto err_free_private; + } + DRM_INFO("Exynos DRM: using %s device for DMA mapping operations\n", + dev_name(private->dma_dev)); + + /* create common IOMMU mapping for all devices attached to Exynos DRM */ + ret = drm_create_iommu_mapping(drm); + if (ret < 0) { + DRM_ERROR("failed to create iommu mapping.\n"); + goto err_free_private; + } + + drm_mode_config_init(drm); + + exynos_drm_mode_config_init(drm); + + /* setup possible_clones. */ + cnt = 0; + clone_mask = 0; + list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) + clone_mask |= (1 << (cnt++)); + + list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) + encoder->possible_clones = clone_mask; + + /* Try to bind all sub drivers. */ + ret = component_bind_all(drm->dev, drm); + if (ret) + goto err_mode_config_cleanup; + + ret = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (ret) + goto err_unbind_all; + + /* Probe non kms sub drivers and virtual display driver. */ + ret = exynos_drm_device_subdrv_probe(drm); + if (ret) + goto err_cleanup_vblank; + + drm_mode_config_reset(drm); + + /* + * enable drm irq mode. + * - with irq_enabled = true, we can use the vblank feature. + * + * P.S. note that we wouldn't use drm irq handler but + * just specific driver own one instead because + * drm framework supports only one irq handler. + */ + drm->irq_enabled = true; + + /* init kms poll for handling hpd */ + drm_kms_helper_poll_init(drm); + + /* force connectors detection */ + drm_helper_hpd_irq_event(drm); + + /* register the DRM device */ + ret = drm_dev_register(drm, 0); + if (ret < 0) + goto err_cleanup_fbdev; + + return 0; + +err_cleanup_fbdev: + exynos_drm_fbdev_fini(drm); + drm_kms_helper_poll_fini(drm); + exynos_drm_device_subdrv_remove(drm); +err_cleanup_vblank: + drm_vblank_cleanup(drm); +err_unbind_all: + component_unbind_all(drm->dev, drm); +err_mode_config_cleanup: + drm_mode_config_cleanup(drm); + drm_release_iommu_mapping(drm); +err_free_private: + kfree(private); +err_free_drm: + drm_dev_unref(drm); + + return ret; } static void exynos_drm_unbind(struct device *dev) { - drm_put_dev(dev_get_drvdata(dev)); + struct drm_device *drm = dev_get_drvdata(dev); + + drm_dev_unregister(drm); + + exynos_drm_device_subdrv_remove(drm); + + exynos_drm_fbdev_fini(drm); + drm_kms_helper_poll_fini(drm); + + component_unbind_all(drm->dev, drm); + drm_mode_config_cleanup(drm); + drm_release_iommu_mapping(drm); + + kfree(drm->dev_private); + drm->dev_private = NULL; + + drm_dev_unref(drm); } static const struct component_master_ops exynos_drm_ops = { diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 812e2ec0761d..43a45abc524f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1587,7 +1587,6 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder) } drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs); - drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 40aa5056d4ae..641531243e04 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -119,7 +119,6 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, struct exynos_drm_gem *exynos_gem; struct drm_device *dev = helper->dev; struct drm_mode_fb_cmd2 mode_cmd = { 0 }; - struct platform_device *pdev = dev->platformdev; unsigned long size; int ret; @@ -142,7 +141,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, * memory area. */ if (IS_ERR(exynos_gem) && is_drm_iommu_supported(dev)) { - dev_warn(&pdev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n"); + dev_warn(dev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n"); exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_NONCONTIG, size); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 57fe514d5c5b..6bbb0ea8a6af 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -359,7 +359,6 @@ static int vidi_create_connector(struct drm_encoder *encoder) } drm_connector_helper_add(connector, &vidi_connector_helper_funcs); - drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 0814ed76445c..52438404c8c9 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -920,7 +920,6 @@ static int hdmi_create_connector(struct drm_encoder *encoder) } drm_connector_helper_add(connector, &hdmi_connector_helper_funcs); - drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); if (hdata->bridge) { diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index f645275e6e63..f039641070ac 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -175,7 +175,6 @@ static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = { .mpll_cfg = imx_mpll_cfg, .cur_ctr = imx_cur_ctr, .phy_config = imx_phy_config, - .dev_type = IMX6Q_HDMI, .mode_valid = imx6q_hdmi_mode_valid, }; @@ -183,7 +182,6 @@ static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = { .mpll_cfg = imx_mpll_cfg, .cur_ctr = imx_cur_ctr, .phy_config = imx_phy_config, - .dev_type = IMX6DL_HDMI, .mode_valid = imx6dl_hdmi_mode_valid, }; diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 4b7b92a7bcf7..b6dbcd17f1e6 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -30,14 +30,10 @@ #include <video/imx-ipu-v3.h> #include "imx-drm.h" +#include "ipuv3-plane.h" #define MAX_CRTC 4 -struct imx_drm_component { - struct device_node *of_node; - struct list_head list; -}; - struct imx_drm_device { struct drm_device *drm; unsigned int pipes; @@ -109,6 +105,11 @@ static int imx_drm_atomic_check(struct drm_device *dev, if (ret) return ret; + /* Assign PRG/PRE channels and check if all constrains are satisfied. */ + ret = ipu_planes_assign_pre(dev, state); + if (ret) + return ret; + return ret; } @@ -122,6 +123,10 @@ static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = { static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state) { struct drm_device *dev = state->dev; + struct drm_plane *plane; + struct drm_plane_state *old_plane_state; + bool plane_disabling = false; + int i; drm_atomic_helper_commit_modeset_disables(dev, state); @@ -131,11 +136,20 @@ static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_enables(dev, state); - drm_atomic_helper_commit_hw_done(state); + for_each_plane_in_state(state, plane, old_plane_state, i) { + if (drm_atomic_plane_disabling(old_plane_state, plane->state)) + plane_disabling = true; + } + + if (plane_disabling) { + drm_atomic_helper_wait_for_vblanks(dev, state); - drm_atomic_helper_wait_for_vblanks(dev, state); + for_each_plane_in_state(state, plane, old_plane_state, i) + ipu_plane_disable_deferred(plane); - drm_atomic_helper_cleanup_planes(dev, state); + } + + drm_atomic_helper_commit_hw_done(state); } static const struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = { diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h index cc003334505d..295434b199db 100644 --- a/drivers/gpu/drm/imx/imx-drm.h +++ b/drivers/gpu/drm/imx/imx-drm.h @@ -39,4 +39,7 @@ int imx_drm_encoder_parse_of(struct drm_device *drm, void imx_drm_connector_destroy(struct drm_connector *connector); void imx_drm_encoder_destroy(struct drm_encoder *encoder); +int ipu_planes_assign_pre(struct drm_device *dev, + struct drm_atomic_state *state); + #endif /* _IMX_DRM_H_ */ diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index a3f2843b78cd..dab9d50ffd8c 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -55,11 +55,32 @@ static void ipu_crtc_enable(struct drm_crtc *crtc) struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); + ipu_prg_enable(ipu); ipu_dc_enable(ipu); ipu_dc_enable_channel(ipu_crtc->dc); ipu_di_enable(ipu_crtc->di); } +static void ipu_crtc_disable_planes(struct ipu_crtc *ipu_crtc, + struct drm_crtc_state *old_crtc_state) +{ + bool disable_partial = false; + bool disable_full = false; + struct drm_plane *plane; + + drm_atomic_crtc_state_for_each_plane(plane, old_crtc_state) { + if (plane == &ipu_crtc->plane[0]->base) + disable_full = true; + if (&ipu_crtc->plane[1] && plane == &ipu_crtc->plane[1]->base) + disable_partial = true; + } + + if (disable_partial) + ipu_plane_disable(ipu_crtc->plane[1], true); + if (disable_full) + ipu_plane_disable(ipu_crtc->plane[0], false); +} + static void ipu_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { @@ -73,8 +94,9 @@ static void ipu_crtc_atomic_disable(struct drm_crtc *crtc, * attached IDMACs will be left in undefined state, possibly hanging * the IPU or even system. */ - drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); + ipu_crtc_disable_planes(ipu_crtc, old_crtc_state); ipu_dc_disable(ipu); + ipu_prg_disable(ipu); spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) { diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 8b5294d47cee..d63e853a0300 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -23,6 +23,17 @@ #include "video/imx-ipu-v3.h" #include "ipuv3-plane.h" +struct ipu_plane_state { + struct drm_plane_state base; + bool use_pre; +}; + +static inline struct ipu_plane_state * +to_ipu_plane_state(struct drm_plane_state *p) +{ + return container_of(p, struct ipu_plane_state, base); +} + static inline struct ipu_plane *to_ipu_plane(struct drm_plane *p) { return container_of(p, struct ipu_plane, base); @@ -57,6 +68,12 @@ static const uint32_t ipu_plane_formats[] = { DRM_FORMAT_NV12, DRM_FORMAT_NV16, DRM_FORMAT_RGB565, + DRM_FORMAT_RGB565_A8, + DRM_FORMAT_BGR565_A8, + DRM_FORMAT_RGB888_A8, + DRM_FORMAT_BGR888_A8, + DRM_FORMAT_RGBX8888_A8, + DRM_FORMAT_BGRX8888_A8, }; int ipu_plane_irq(struct ipu_plane *ipu_plane) @@ -66,18 +83,18 @@ int ipu_plane_irq(struct ipu_plane *ipu_plane) } static inline unsigned long -drm_plane_state_to_eba(struct drm_plane_state *state) +drm_plane_state_to_eba(struct drm_plane_state *state, int plane) { struct drm_framebuffer *fb = state->fb; struct drm_gem_cma_object *cma_obj; - int x = state->src_x >> 16; - int y = state->src_y >> 16; + int x = state->src.x1 >> 16; + int y = state->src.y1 >> 16; - cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + cma_obj = drm_fb_cma_get_gem_obj(fb, plane); BUG_ON(!cma_obj); - return cma_obj->paddr + fb->offsets[0] + fb->pitches[0] * y + - fb->format->cpp[0] * x; + return cma_obj->paddr + fb->offsets[plane] + fb->pitches[plane] * y + + fb->format->cpp[plane] * x; } static inline unsigned long @@ -85,9 +102,9 @@ drm_plane_state_to_ubo(struct drm_plane_state *state) { struct drm_framebuffer *fb = state->fb; struct drm_gem_cma_object *cma_obj; - unsigned long eba = drm_plane_state_to_eba(state); - int x = state->src_x >> 16; - int y = state->src_y >> 16; + unsigned long eba = drm_plane_state_to_eba(state, 0); + int x = state->src.x1 >> 16; + int y = state->src.y1 >> 16; cma_obj = drm_fb_cma_get_gem_obj(fb, 1); BUG_ON(!cma_obj); @@ -104,9 +121,9 @@ drm_plane_state_to_vbo(struct drm_plane_state *state) { struct drm_framebuffer *fb = state->fb; struct drm_gem_cma_object *cma_obj; - unsigned long eba = drm_plane_state_to_eba(state); - int x = state->src_x >> 16; - int y = state->src_y >> 16; + unsigned long eba = drm_plane_state_to_eba(state, 0); + int x = state->src.x1 >> 16; + int y = state->src.y1 >> 16; cma_obj = drm_fb_cma_get_gem_obj(fb, 2); BUG_ON(!cma_obj); @@ -126,11 +143,14 @@ void ipu_plane_put_resources(struct ipu_plane *ipu_plane) ipu_dmfc_put(ipu_plane->dmfc); if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch)) ipu_idmac_put(ipu_plane->ipu_ch); + if (!IS_ERR_OR_NULL(ipu_plane->alpha_ch)) + ipu_idmac_put(ipu_plane->alpha_ch); } int ipu_plane_get_resources(struct ipu_plane *ipu_plane) { int ret; + int alpha_ch; ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma); if (IS_ERR(ipu_plane->ipu_ch)) { @@ -139,6 +159,17 @@ int ipu_plane_get_resources(struct ipu_plane *ipu_plane) return ret; } + alpha_ch = ipu_channel_alpha_channel(ipu_plane->dma); + if (alpha_ch >= 0) { + ipu_plane->alpha_ch = ipu_idmac_get(ipu_plane->ipu, alpha_ch); + if (IS_ERR(ipu_plane->alpha_ch)) { + ret = PTR_ERR(ipu_plane->alpha_ch); + DRM_ERROR("failed to get alpha idmac channel %d: %d\n", + alpha_ch, ret); + return ret; + } + } + ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma); if (IS_ERR(ipu_plane->dmfc)) { ret = PTR_ERR(ipu_plane->dmfc); @@ -162,33 +193,61 @@ err_out: return ret; } +static bool ipu_plane_separate_alpha(struct ipu_plane *ipu_plane) +{ + switch (ipu_plane->base.state->fb->format->format) { + case DRM_FORMAT_RGB565_A8: + case DRM_FORMAT_BGR565_A8: + case DRM_FORMAT_RGB888_A8: + case DRM_FORMAT_BGR888_A8: + case DRM_FORMAT_RGBX8888_A8: + case DRM_FORMAT_BGRX8888_A8: + return true; + default: + return false; + } +} + static void ipu_plane_enable(struct ipu_plane *ipu_plane) { if (ipu_plane->dp) ipu_dp_enable(ipu_plane->ipu); ipu_dmfc_enable_channel(ipu_plane->dmfc); ipu_idmac_enable_channel(ipu_plane->ipu_ch); + if (ipu_plane_separate_alpha(ipu_plane)) + ipu_idmac_enable_channel(ipu_plane->alpha_ch); if (ipu_plane->dp) ipu_dp_enable_channel(ipu_plane->dp); } -static int ipu_disable_plane(struct drm_plane *plane) +void ipu_plane_disable(struct ipu_plane *ipu_plane, bool disable_dp_channel) { - struct ipu_plane *ipu_plane = to_ipu_plane(plane); - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50); - if (ipu_plane->dp) - ipu_dp_disable_channel(ipu_plane->dp); + if (ipu_plane->dp && disable_dp_channel) + ipu_dp_disable_channel(ipu_plane->dp, false); ipu_idmac_disable_channel(ipu_plane->ipu_ch); + if (ipu_plane->alpha_ch) + ipu_idmac_disable_channel(ipu_plane->alpha_ch); ipu_dmfc_disable_channel(ipu_plane->dmfc); if (ipu_plane->dp) ipu_dp_disable(ipu_plane->ipu); + if (ipu_prg_present(ipu_plane->ipu)) + ipu_prg_channel_disable(ipu_plane->ipu_ch); +} - return 0; +void ipu_plane_disable_deferred(struct drm_plane *plane) +{ + struct ipu_plane *ipu_plane = to_ipu_plane(plane); + + if (ipu_plane->disabling) { + ipu_plane->disabling = false; + ipu_plane_disable(ipu_plane, false); + } } +EXPORT_SYMBOL_GPL(ipu_plane_disable_deferred); static void ipu_plane_destroy(struct drm_plane *plane) { @@ -200,13 +259,56 @@ static void ipu_plane_destroy(struct drm_plane *plane) kfree(ipu_plane); } +void ipu_plane_state_reset(struct drm_plane *plane) +{ + struct ipu_plane_state *ipu_state; + + if (plane->state) { + ipu_state = to_ipu_plane_state(plane->state); + __drm_atomic_helper_plane_destroy_state(plane->state); + kfree(ipu_state); + } + + ipu_state = kzalloc(sizeof(*ipu_state), GFP_KERNEL); + + if (ipu_state) { + ipu_state->base.plane = plane; + ipu_state->base.rotation = DRM_ROTATE_0; + } + + plane->state = &ipu_state->base; +} + +struct drm_plane_state *ipu_plane_duplicate_state(struct drm_plane *plane) +{ + struct ipu_plane_state *state; + + if (WARN_ON(!plane->state)) + return NULL; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_plane_duplicate_state(plane, &state->base); + + return &state->base; +} + +void ipu_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct ipu_plane_state *ipu_state = to_ipu_plane_state(state); + + __drm_atomic_helper_plane_destroy_state(state); + kfree(ipu_state); +} + static const struct drm_plane_funcs ipu_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = ipu_plane_destroy, - .reset = drm_atomic_helper_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .reset = ipu_plane_state_reset, + .atomic_duplicate_state = ipu_plane_duplicate_state, + .atomic_destroy_state = ipu_plane_destroy_state, }; static int ipu_plane_atomic_check(struct drm_plane *plane, @@ -217,8 +319,11 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, struct device *dev = plane->dev->dev; struct drm_framebuffer *fb = state->fb; struct drm_framebuffer *old_fb = old_state->fb; - unsigned long eba, ubo, vbo, old_ubo, old_vbo; + unsigned long eba, ubo, vbo, old_ubo, old_vbo, alpha_eba; + bool can_position = (plane->type == DRM_PLANE_TYPE_OVERLAY); + struct drm_rect clip; int hsub, vsub; + int ret; /* Ok to disable */ if (!fb) @@ -232,44 +337,35 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, if (WARN_ON(!crtc_state)) return -EINVAL; + clip.x1 = 0; + clip.y1 = 0; + clip.x2 = crtc_state->adjusted_mode.hdisplay; + clip.y2 = crtc_state->adjusted_mode.vdisplay; + ret = drm_plane_helper_check_state(state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + can_position, true); + if (ret) + return ret; + /* CRTC should be enabled */ if (!crtc_state->enable) return -EINVAL; - /* no scaling */ - if (state->src_w >> 16 != state->crtc_w || - state->src_h >> 16 != state->crtc_h) - return -EINVAL; - switch (plane->type) { case DRM_PLANE_TYPE_PRIMARY: - /* full plane doesn't support partial off screen */ - if (state->crtc_x || state->crtc_y || - state->crtc_w != crtc_state->adjusted_mode.hdisplay || - state->crtc_h != crtc_state->adjusted_mode.vdisplay) - return -EINVAL; - /* full plane minimum width is 13 pixels */ - if (state->crtc_w < 13) + if (drm_rect_width(&state->dst) < 13) return -EINVAL; break; case DRM_PLANE_TYPE_OVERLAY: - if (state->crtc_x < 0 || state->crtc_y < 0) - return -EINVAL; - - if (state->crtc_x + state->crtc_w > - crtc_state->adjusted_mode.hdisplay) - return -EINVAL; - if (state->crtc_y + state->crtc_h > - crtc_state->adjusted_mode.vdisplay) - return -EINVAL; break; default: - dev_warn(dev, "Unsupported plane type\n"); + dev_warn(dev, "Unsupported plane type %d\n", plane->type); return -EINVAL; } - if (state->crtc_h < 2) + if (drm_rect_height(&state->dst) < 2) return -EINVAL; /* @@ -279,12 +375,13 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, * callback. The planes will be reenabled in plane's ->atomic_update * callback. */ - if (old_fb && (state->src_w != old_state->src_w || - state->src_h != old_state->src_h || - fb->format != old_fb->format)) + if (old_fb && + (drm_rect_width(&state->dst) != drm_rect_width(&old_state->dst) || + drm_rect_height(&state->dst) != drm_rect_height(&old_state->dst) || + fb->format != old_fb->format)) crtc_state->mode_changed = true; - eba = drm_plane_state_to_eba(state); + eba = drm_plane_state_to_eba(state, 0); if (eba & 0x7) return -EINVAL; @@ -350,9 +447,26 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, */ hsub = drm_format_horz_chroma_subsampling(fb->format->format); vsub = drm_format_vert_chroma_subsampling(fb->format->format); - if (((state->src_x >> 16) & (hsub - 1)) || - ((state->src_y >> 16) & (vsub - 1))) + if (((state->src.x1 >> 16) & (hsub - 1)) || + ((state->src.y1 >> 16) & (vsub - 1))) return -EINVAL; + break; + case DRM_FORMAT_RGB565_A8: + case DRM_FORMAT_BGR565_A8: + case DRM_FORMAT_RGB888_A8: + case DRM_FORMAT_BGR888_A8: + case DRM_FORMAT_RGBX8888_A8: + case DRM_FORMAT_BGRX8888_A8: + alpha_eba = drm_plane_state_to_eba(state, 1); + if (alpha_eba & 0x7) + return -EINVAL; + + if (fb->pitches[1] < 1 || fb->pitches[1] > 16384) + return -EINVAL; + + if (old_fb && old_fb->pitches[1] != fb->pitches[1]) + crtc_state->mode_changed = true; + break; } return 0; @@ -361,7 +475,25 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, static void ipu_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { - ipu_disable_plane(plane); + struct ipu_plane *ipu_plane = to_ipu_plane(plane); + + if (ipu_plane->dp) + ipu_dp_disable_channel(ipu_plane->dp, true); + ipu_plane->disabling = true; +} + +static int ipu_chan_assign_axi_id(int ipu_chan) +{ + switch (ipu_chan) { + case IPUV3_CHANNEL_MEM_BG_SYNC: + return 1; + case IPUV3_CHANNEL_MEM_FG_SYNC: + return 2; + case IPUV3_CHANNEL_MEM_DC_SYNC: + return 3; + default: + return 0; + } } static void ipu_plane_atomic_update(struct drm_plane *plane, @@ -369,18 +501,47 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, { struct ipu_plane *ipu_plane = to_ipu_plane(plane); struct drm_plane_state *state = plane->state; + struct ipu_plane_state *ipu_state = to_ipu_plane_state(state); struct drm_crtc_state *crtc_state = state->crtc->state; struct drm_framebuffer *fb = state->fb; + struct drm_rect *dst = &state->dst; unsigned long eba, ubo, vbo; + unsigned long alpha_eba = 0; enum ipu_color_space ics; + unsigned int axi_id = 0; int active; - eba = drm_plane_state_to_eba(state); + if (ipu_plane->dp_flow == IPU_DP_FLOW_SYNC_FG) + ipu_dp_set_window_pos(ipu_plane->dp, dst->x1, dst->y1); + + eba = drm_plane_state_to_eba(state, 0); + + /* + * Configure PRG channel and attached PRE, this changes the EBA to an + * internal SRAM location. + */ + if (ipu_state->use_pre) { + axi_id = ipu_chan_assign_axi_id(ipu_plane->dma); + ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id, + drm_rect_width(&state->src) >> 16, + drm_rect_height(&state->src) >> 16, + state->fb->pitches[0], + state->fb->format->format, &eba); + } if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state)) { + /* nothing to do if PRE is used */ + if (ipu_state->use_pre) + return; active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch); ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba); ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active); + if (ipu_plane_separate_alpha(ipu_plane)) { + active = ipu_idmac_get_current_buffer(ipu_plane->alpha_ch); + ipu_cpmem_set_buffer(ipu_plane->alpha_ch, !active, + alpha_eba); + ipu_idmac_select_buffer(ipu_plane->alpha_ch, !active); + } return; } @@ -395,8 +556,6 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ics = ipu_drm_fourcc_to_colorspace(state->fb->format->format); ipu_dp_setup_channel(ipu_plane->dp, ics, IPUV3_COLORSPACE_UNKNOWN); - ipu_dp_set_window_pos(ipu_plane->dp, state->crtc_x, - state->crtc_y); /* Enable local alpha on partial plane */ switch (state->fb->format->format) { case DRM_FORMAT_ARGB1555: @@ -408,6 +567,12 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, case DRM_FORMAT_ABGR8888: case DRM_FORMAT_RGBA8888: case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_RGB565_A8: + case DRM_FORMAT_BGR565_A8: + case DRM_FORMAT_RGB888_A8: + case DRM_FORMAT_BGR888_A8: + case DRM_FORMAT_RGBX8888_A8: + case DRM_FORMAT_BGRX8888_A8: ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false); break; default: @@ -416,15 +581,17 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, } } - ipu_dmfc_config_wait4eot(ipu_plane->dmfc, state->crtc_w); + ipu_dmfc_config_wait4eot(ipu_plane->dmfc, drm_rect_width(dst)); ipu_cpmem_zero(ipu_plane->ipu_ch); - ipu_cpmem_set_resolution(ipu_plane->ipu_ch, state->src_w >> 16, - state->src_h >> 16); + ipu_cpmem_set_resolution(ipu_plane->ipu_ch, + drm_rect_width(&state->src) >> 16, + drm_rect_height(&state->src) >> 16); ipu_cpmem_set_fmt(ipu_plane->ipu_ch, state->fb->format->format); ipu_cpmem_set_high_priority(ipu_plane->ipu_ch); ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1); ipu_cpmem_set_stride(ipu_plane->ipu_ch, state->fb->pitches[0]); + ipu_cpmem_set_axi_id(ipu_plane->ipu_ch, axi_id); switch (fb->format->format) { case DRM_FORMAT_YUV420: case DRM_FORMAT_YVU420: @@ -444,7 +611,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, dev_dbg(ipu_plane->base.dev->dev, "phy = %lu %lu %lu, x = %d, y = %d", eba, ubo, vbo, - state->src_x >> 16, state->src_y >> 16); + state->src.x1 >> 16, state->src.y1 >> 16); break; case DRM_FORMAT_NV12: case DRM_FORMAT_NV16: @@ -455,11 +622,37 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, dev_dbg(ipu_plane->base.dev->dev, "phy = %lu %lu, x = %d, y = %d", eba, ubo, - state->src_x >> 16, state->src_y >> 16); + state->src.x1 >> 16, state->src.y1 >> 16); + break; + case DRM_FORMAT_RGB565_A8: + case DRM_FORMAT_BGR565_A8: + case DRM_FORMAT_RGB888_A8: + case DRM_FORMAT_BGR888_A8: + case DRM_FORMAT_RGBX8888_A8: + case DRM_FORMAT_BGRX8888_A8: + alpha_eba = drm_plane_state_to_eba(state, 1); + + dev_dbg(ipu_plane->base.dev->dev, "phys = %lu %lu, x = %d, y = %d", + eba, alpha_eba, state->src.x1 >> 16, state->src.y1 >> 16); + + ipu_cpmem_set_burstsize(ipu_plane->ipu_ch, 16); + + ipu_cpmem_zero(ipu_plane->alpha_ch); + ipu_cpmem_set_resolution(ipu_plane->alpha_ch, + drm_rect_width(&state->src) >> 16, + drm_rect_height(&state->src) >> 16); + ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8); + ipu_cpmem_set_high_priority(ipu_plane->alpha_ch); + ipu_idmac_set_double_buffer(ipu_plane->alpha_ch, 1); + ipu_cpmem_set_stride(ipu_plane->alpha_ch, + state->fb->pitches[1]); + ipu_cpmem_set_burstsize(ipu_plane->alpha_ch, 16); + ipu_cpmem_set_buffer(ipu_plane->alpha_ch, 0, alpha_eba); + ipu_cpmem_set_buffer(ipu_plane->alpha_ch, 1, alpha_eba); break; default: dev_dbg(ipu_plane->base.dev->dev, "phys = %lu, x = %d, y = %d", - eba, state->src_x >> 16, state->src_y >> 16); + eba, state->src.x1 >> 16, state->src.y1 >> 16); break; } ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba); @@ -474,6 +667,35 @@ static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = { .atomic_update = ipu_plane_atomic_update, }; +int ipu_planes_assign_pre(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state; + struct drm_plane *plane; + int available_pres = ipu_prg_max_active_channels(); + int i; + + for_each_plane_in_state(state, plane, plane_state, i) { + struct ipu_plane_state *ipu_state = + to_ipu_plane_state(plane_state); + struct ipu_plane *ipu_plane = to_ipu_plane(plane); + + if (ipu_prg_present(ipu_plane->ipu) && available_pres && + plane_state->fb && + ipu_prg_format_supported(ipu_plane->ipu, + plane_state->fb->format->format, + plane_state->fb->modifier)) { + ipu_state->use_pre = true; + available_pres--; + } else { + ipu_state->use_pre = false; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_planes_assign_pre); + struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, int dma, int dp, unsigned int possible_crtcs, enum drm_plane_type type) diff --git a/drivers/gpu/drm/imx/ipuv3-plane.h b/drivers/gpu/drm/imx/ipuv3-plane.h index 338b88a74eb6..596b24ddbf65 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.h +++ b/drivers/gpu/drm/imx/ipuv3-plane.h @@ -18,11 +18,14 @@ struct ipu_plane { struct ipu_soc *ipu; struct ipuv3_channel *ipu_ch; + struct ipuv3_channel *alpha_ch; struct dmfc_channel *dmfc; struct ipu_dp *dp; int dma; int dp_flow; + + bool disabling; }; struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, @@ -42,4 +45,7 @@ void ipu_plane_put_resources(struct ipu_plane *plane); int ipu_plane_irq(struct ipu_plane *plane); +void ipu_plane_disable(struct ipu_plane *ipu_plane, bool disable_dp_channel); +void ipu_plane_disable_deferred(struct drm_plane *plane); + #endif diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 94ea963519b2..a4e1206a66a8 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -434,7 +434,7 @@ fail: struct msm_kms *mdp4_kms_init(struct drm_device *dev) { - struct platform_device *pdev = dev->platformdev; + struct platform_device *pdev = to_platform_device(dev->dev); struct mdp4_platform_config *config = mdp4_get_config(pdev); struct mdp4_kms *mdp4_kms; struct msm_kms *kms = NULL; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c index 34ab553f6897..ba2d017f6591 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c @@ -505,7 +505,7 @@ struct mdp5_cfg_handler *mdp5_cfg_init(struct mdp5_kms *mdp5_kms, uint32_t major, uint32_t minor) { struct drm_device *dev = mdp5_kms->dev; - struct platform_device *pdev = dev->platformdev; + struct platform_device *pdev = to_platform_device(dev->dev); struct mdp5_cfg_handler *cfg_handler; struct mdp5_cfg_platform *pconfig; int i, ret = 0; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c index d444a6901fff..f8f48d014978 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c @@ -160,7 +160,7 @@ void msm_mdss_destroy(struct drm_device *dev) int msm_mdss_init(struct drm_device *dev) { - struct platform_device *pdev = dev->platformdev; + struct platform_device *pdev = to_platform_device(dev->dev); struct msm_drm_private *priv = dev->dev_private; struct msm_mdss *mdss; int ret; diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c index 75609a1debf7..4f35d4eb85d0 100644 --- a/drivers/gpu/drm/msm/msm_debugfs.c +++ b/drivers/gpu/drm/msm/msm_debugfs.c @@ -164,20 +164,5 @@ int msm_debugfs_init(struct drm_minor *minor) return ret; } - -void msm_debugfs_cleanup(struct drm_minor *minor) -{ - struct drm_device *dev = minor->dev; - struct msm_drm_private *priv = dev->dev_private; - - if (!priv) - return; - - if (priv->kms->funcs->debugfs_cleanup) - priv->kms->funcs->debugfs_cleanup(priv->kms, minor); - - msm_rd_debugfs_cleanup(minor); - msm_perf_debugfs_cleanup(minor); -} #endif diff --git a/drivers/gpu/drm/msm/msm_debugfs.h b/drivers/gpu/drm/msm/msm_debugfs.h index 6110c972fd15..f4077e344e3a 100644 --- a/drivers/gpu/drm/msm/msm_debugfs.h +++ b/drivers/gpu/drm/msm/msm_debugfs.h @@ -20,7 +20,6 @@ #ifdef CONFIG_DEBUG_FS int msm_debugfs_init(struct drm_minor *minor); -void msm_debugfs_cleanup(struct drm_minor *minor); #endif #endif /* __MSM_DEBUGFS_H__ */ diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 6842d427cc2b..9208e67be453 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -241,6 +241,9 @@ static int msm_drm_uninit(struct device *dev) drm_dev_unregister(ddev); + msm_perf_debugfs_cleanup(priv); + msm_rd_debugfs_cleanup(priv); + #ifdef CONFIG_DRM_FBDEV_EMULATION if (fbdev && priv->fbdev) msm_fbdev_free(ddev); @@ -383,7 +386,6 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) } platform_set_drvdata(pdev, ddev); - ddev->platformdev = pdev; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { @@ -836,7 +838,6 @@ static struct drm_driver msm_driver = { .gem_prime_mmap = msm_gem_prime_mmap, #ifdef CONFIG_DEBUG_FS .debugfs_init = msm_debugfs_init, - .debugfs_cleanup = msm_debugfs_cleanup, #endif .ioctls = msm_ioctls, .num_ioctls = DRM_MSM_NUM_IOCTLS, diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index c3b14876edaa..b51fb0d70f43 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -304,10 +304,10 @@ void msm_gem_describe_objects(struct list_head *list, struct seq_file *m); void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m); int msm_debugfs_late_init(struct drm_device *dev); int msm_rd_debugfs_init(struct drm_minor *minor); -void msm_rd_debugfs_cleanup(struct drm_minor *minor); +void msm_rd_debugfs_cleanup(struct msm_drm_private *priv); void msm_rd_dump_submit(struct msm_gem_submit *submit); int msm_perf_debugfs_init(struct drm_minor *minor); -void msm_perf_debugfs_cleanup(struct drm_minor *minor); +void msm_perf_debugfs_cleanup(struct msm_drm_private *priv); #else static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; } static inline void msm_rd_dump_submit(struct msm_gem_submit *submit) {} diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index 117635d2b8c5..faa22c7c5423 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -64,7 +64,6 @@ struct msm_kms_funcs { #ifdef CONFIG_DEBUG_FS /* debugfs: */ int (*debugfs_init)(struct msm_kms *kms, struct drm_minor *minor); - void (*debugfs_cleanup)(struct msm_kms *kms, struct drm_minor *minor); #endif }; diff --git a/drivers/gpu/drm/msm/msm_perf.c b/drivers/gpu/drm/msm/msm_perf.c index fc5a948c124c..5ab21bd2decb 100644 --- a/drivers/gpu/drm/msm/msm_perf.c +++ b/drivers/gpu/drm/msm/msm_perf.c @@ -231,13 +231,12 @@ int msm_perf_debugfs_init(struct drm_minor *minor) return 0; fail: - msm_perf_debugfs_cleanup(minor); + msm_perf_debugfs_cleanup(priv); return -1; } -void msm_perf_debugfs_cleanup(struct drm_minor *minor) +void msm_perf_debugfs_cleanup(struct msm_drm_private *priv) { - struct msm_drm_private *priv = minor->dev->dev_private; struct msm_perf_state *perf = priv->perf; if (!perf) diff --git a/drivers/gpu/drm/msm/msm_rd.c b/drivers/gpu/drm/msm/msm_rd.c index ab0b39f56780..3df7322fd74e 100644 --- a/drivers/gpu/drm/msm/msm_rd.c +++ b/drivers/gpu/drm/msm/msm_rd.c @@ -245,13 +245,12 @@ int msm_rd_debugfs_init(struct drm_minor *minor) return 0; fail: - msm_rd_debugfs_cleanup(minor); + msm_rd_debugfs_cleanup(priv); return -1; } -void msm_rd_debugfs_cleanup(struct drm_minor *minor) +void msm_rd_debugfs_cleanup(struct msm_drm_private *priv) { - struct msm_drm_private *priv = minor->dev->dev_private; struct msm_rd_state *rd = priv->rd; if (!rd) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index f0bb7606eb8b..2b6ac24ce690 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -108,7 +108,7 @@ nouveau_name(struct drm_device *dev) if (dev->pdev) return nouveau_pci_name(dev->pdev); else - return nouveau_platform_name(dev->platformdev); + return nouveau_platform_name(to_platform_device(dev->dev)); } static void @@ -1095,7 +1095,6 @@ nouveau_platform_device_create(const struct nvkm_device_tegra_func *func, goto err_free; } - drm->platformdev = pdev; platform_set_drvdata(pdev, drm); return drm; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c index 814daf35e21f..f865d2a3e184 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c @@ -59,6 +59,13 @@ static void nvkm_sec2_recv(struct work_struct *work) { struct nvkm_sec2 *sec2 = container_of(work, typeof(*sec2), work); + + if (!sec2->queue) { + nvkm_warn(&sec2->engine.subdev, + "recv function called while no firmware set!\n"); + return; + } + nvkm_msgqueue_recv(sec2->queue); } diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.c b/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.c index a063fb823117..982efedb4b13 100644 --- a/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.c +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.c @@ -510,11 +510,10 @@ nvkm_msgqueue_del(struct nvkm_msgqueue **queue) void nvkm_msgqueue_recv(struct nvkm_msgqueue *queue) { - if (!queue || !queue->func || !queue->func->recv) { + if (!queue->func || !queue->func->recv) { const struct nvkm_subdev *subdev = queue->falcon->owner; - nvkm_warn(subdev, - "cmdqueue recv function called while no firmware set!\n"); + nvkm_warn(subdev, "missing msgqueue recv function\n"); return; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c index 48ae02d45656..44bef22bce52 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c @@ -27,6 +27,12 @@ static void gm20b_pmu_recv(struct nvkm_pmu *pmu) { + if (!pmu->queue) { + nvkm_warn(&pmu->subdev, + "recv function called while no firmware set!\n"); + return; + } + nvkm_msgqueue_recv(pmu->queue); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gp102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gp102.c index 8570c84c8a29..f3b3c66349d2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gp102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gp102.c @@ -59,7 +59,8 @@ gp102_run_secure_scrub(struct nvkm_secboot *sb) nvkm_debug(subdev, "running VPR scrubber binary on NVDEC...\n"); - if (!(engine = nvkm_engine_ref(&device->nvdec->engine))) + engine = nvkm_engine_ref(&device->nvdec->engine); + if (IS_ERR(engine)) return PTR_ERR(engine); falcon = device->nvdec->falcon; diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c index 8e6c78003226..ffe821b61f7d 100644 --- a/drivers/gpu/drm/qxl/qxl_debugfs.c +++ b/drivers/gpu/drm/qxl/qxl_debugfs.c @@ -129,16 +129,3 @@ int qxl_debugfs_add_files(struct qxl_device *qdev, #endif return 0; } - -void qxl_debugfs_remove_files(struct qxl_device *qdev) -{ -#if defined(CONFIG_DEBUG_FS) - unsigned i; - - for (i = 0; i < qdev->debugfs_count; i++) { - drm_debugfs_remove_files(qdev->debugfs[i].files, - qdev->debugfs[i].num_files, - qdev->ddev.primary); - } -#endif -} diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 2cd14bebc49c..9548bb58d3bc 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -81,6 +81,10 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) qdev->rom->client_monitors_config_crc); return MONITORS_CONFIG_BAD_CRC; } + if (!num_monitors) { + DRM_DEBUG_KMS("no client monitors configured\n"); + return status; + } if (num_monitors > qdev->monitors_config->max_allowed) { DRM_DEBUG_KMS("client monitors list will be truncated: %d < %d\n", qdev->monitors_config->max_allowed, num_monitors); @@ -157,19 +161,23 @@ static void qxl_update_offset_props(struct qxl_device *qdev) void qxl_display_read_client_monitors_config(struct qxl_device *qdev) { - struct drm_device *dev = &qdev->ddev; - int status; + int status, retries; - status = qxl_display_copy_rom_client_monitors_config(qdev); - while (status == MONITORS_CONFIG_BAD_CRC) { - qxl_io_log(qdev, "failed crc check for client_monitors_config," - " retrying\n"); + for (retries = 0; retries < 10; retries++) { status = qxl_display_copy_rom_client_monitors_config(qdev); + if (status != MONITORS_CONFIG_BAD_CRC) + break; + udelay(5); + } + if (status == MONITORS_CONFIG_BAD_CRC) { + qxl_io_log(qdev, "config: bad crc\n"); + DRM_DEBUG_KMS("ignoring client monitors config: bad crc"); + return; } if (status == MONITORS_CONFIG_UNCHANGED) { - qxl_io_log(qdev, "config unchanged\n"); - DRM_DEBUG("ignoring unchanged client monitors config"); + qxl_io_log(qdev, "config: unchanged\n"); + DRM_DEBUG_KMS("ignoring client monitors config: unchanged"); return; } @@ -194,9 +202,17 @@ static int qxl_add_monitors_config_modes(struct drm_connector *connector, struct drm_display_mode *mode = NULL; struct qxl_head *head; + if (!qdev->monitors_config) + return 0; + if (h >= qdev->monitors_config->max_allowed) + return 0; if (!qdev->client_monitors_config) return 0; + if (h >= qdev->client_monitors_config->count) + return 0; + head = &qdev->client_monitors_config->heads[h]; + DRM_DEBUG_KMS("head %d is %dx%d\n", h, head->width, head->height); mode = drm_cvt_mode(dev, head->width, head->height, 60, false, false, false); @@ -903,19 +919,13 @@ static void qxl_enc_mode_set(struct drm_encoder *encoder, static int qxl_conn_get_modes(struct drm_connector *connector) { - int ret = 0; - struct qxl_device *qdev = connector->dev->dev_private; unsigned pwidth = 1024; unsigned pheight = 768; + int ret = 0; - DRM_DEBUG_KMS("monitors_config=%p\n", qdev->monitors_config); - /* TODO: what should we do here? only show the configured modes for the - * device, or allow the full list, or both? */ - if (qdev->monitors_config && qdev->monitors_config->count) { - ret = qxl_add_monitors_config_modes(connector, &pwidth, &pheight); - if (ret < 0) - return ret; - } + ret = qxl_add_monitors_config_modes(connector, &pwidth, &pheight); + if (ret < 0) + return ret; ret += qxl_add_common_modes(connector, pwidth, pheight); return ret; } @@ -1188,6 +1198,7 @@ int qxl_modeset_init(struct qxl_device *qdev) qdev_output_init(&qdev->ddev, i); } + qxl_display_read_client_monitors_config(qdev); qdev->mode_info.mode_config_initialized = true; drm_mode_config_reset(&qdev->ddev); diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index c0481706e4b0..5ea290a33a68 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -160,8 +160,6 @@ struct qxl_mman { }; struct qxl_mode_info { - int num_modes; - struct qxl_mode *modes; bool mode_config_initialized; /* pointer to fbdev info structure */ @@ -232,7 +230,6 @@ int qxl_debugfs_add_files(struct qxl_device *rdev, struct drm_info_list *files, unsigned nfiles); int qxl_debugfs_fence_init(struct qxl_device *rdev); -void qxl_debugfs_remove_files(struct qxl_device *qdev); struct qxl_device; diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index 35124737666e..14e2a49a4dcf 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -368,9 +368,11 @@ static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = { int qxl_fbdev_init(struct qxl_device *qdev) { + int ret = 0; + +#ifdef CONFIG_DRM_FBDEV_EMULATION struct qxl_fbdev *qfbdev; int bpp_sel = 32; /* TODO: parameter from somewhere? */ - int ret; qfbdev = kzalloc(sizeof(struct qxl_fbdev), GFP_KERNEL); if (!qfbdev) @@ -403,6 +405,8 @@ fini: drm_fb_helper_fini(&qfbdev->helper); free: kfree(qfbdev); +#endif + return ret; } @@ -418,6 +422,9 @@ void qxl_fbdev_fini(struct qxl_device *qdev) void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state) { + if (!qdev->mode_info.qfbdev) + return; + drm_fb_helper_set_suspend(&qdev->mode_info.qfbdev->helper, state); } diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index 2b1e1f3c825d..c5716a0ca3b8 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -31,19 +31,9 @@ int qxl_log_level; -static void qxl_dump_mode(struct qxl_device *qdev, void *p) -{ - struct qxl_mode *m = p; - DRM_DEBUG_KMS("%d: %dx%d %d bits, stride %d, %dmm x %dmm, orientation %d\n", - m->id, m->x_res, m->y_res, m->bits, m->stride, m->x_mili, - m->y_mili, m->orientation); -} - static bool qxl_check_device(struct qxl_device *qdev) { struct qxl_rom *rom = qdev->rom; - int mode_offset; - int i; if (rom->magic != 0x4f525851) { DRM_ERROR("bad rom signature %x\n", rom->magic); @@ -53,8 +43,6 @@ static bool qxl_check_device(struct qxl_device *qdev) DRM_INFO("Device Version %d.%d\n", rom->id, rom->update_id); DRM_INFO("Compression level %d log level %d\n", rom->compression_level, rom->log_level); - DRM_INFO("Currently using mode #%d, list at 0x%x\n", - rom->mode, rom->modes_offset); DRM_INFO("%d io pages at offset 0x%x\n", rom->num_io_pages, rom->pages_offset); DRM_INFO("%d byte draw area at offset 0x%x\n", @@ -62,14 +50,6 @@ static bool qxl_check_device(struct qxl_device *qdev) qdev->vram_size = rom->surface0_area_size; DRM_INFO("RAM header offset: 0x%x\n", rom->ram_header_offset); - - mode_offset = rom->modes_offset / 4; - qdev->mode_info.num_modes = ((u32 *)rom)[mode_offset]; - DRM_INFO("rom modes offset 0x%x for %d modes\n", rom->modes_offset, - qdev->mode_info.num_modes); - qdev->mode_info.modes = (void *)((uint32_t *)rom + mode_offset + 1); - for (i = 0; i < qdev->mode_info.num_modes; i++) - qxl_dump_mode(qdev, qdev->mode_info.modes + i); return true; } @@ -282,7 +262,4 @@ void qxl_device_fini(struct qxl_device *qdev) iounmap(qdev->ram_header); iounmap(qdev->rom); qdev->rom = NULL; - qdev->mode_info.modes = NULL; - qdev->mode_info.num_modes = 0; - qxl_debugfs_remove_files(qdev); } diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index a6d4a0236e8f..d53827413996 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -237,7 +237,6 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = { .mpll_cfg = rockchip_mpll_cfg, .cur_ctr = rockchip_cur_ctr, .phy_config = rockchip_phy_config, - .dev_type = RK3288_HDMI, }; static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 94d7b7327ff7..2151e1cee4b4 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -19,6 +19,9 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_flip_work.h> #include <drm/drm_plane_helper.h> +#ifdef CONFIG_DRM_ANALOGIX_DP +#include <drm/bridge/analogix_dp.h> +#endif #include <linux/kernel.h> #include <linux/module.h> @@ -1111,6 +1114,53 @@ static void vop_crtc_destroy_state(struct drm_crtc *crtc, kfree(s); } +#ifdef CONFIG_DRM_ANALOGIX_DP +static struct drm_connector *vop_get_edp_connector(struct vop *vop) +{ + struct drm_crtc *crtc = &vop->crtc; + struct drm_connector *connector; + + mutex_lock(&crtc->dev->mode_config.mutex); + drm_for_each_connector(connector, crtc->dev) + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { + mutex_unlock(&crtc->dev->mode_config.mutex); + return connector; + } + mutex_unlock(&crtc->dev->mode_config.mutex); + + return NULL; +} + +static int vop_crtc_set_crc_source(struct drm_crtc *crtc, + const char *source_name, size_t *values_cnt) +{ + struct vop *vop = to_vop(crtc); + struct drm_connector *connector; + int ret; + + connector = vop_get_edp_connector(vop); + if (!connector) + return -EINVAL; + + *values_cnt = 3; + + if (source_name && strcmp(source_name, "auto") == 0) + ret = analogix_dp_start_crc(connector); + else if (!source_name) + ret = analogix_dp_stop_crc(connector); + else + ret = -EINVAL; + + return ret; +} +#else +static int vop_crtc_set_crc_source(struct drm_crtc *crtc, + const char *source_name, size_t *values_cnt) +{ + return -ENODEV; +} +#endif + static const struct drm_crtc_funcs vop_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, @@ -1120,6 +1170,7 @@ static const struct drm_crtc_funcs vop_crtc_funcs = { .atomic_destroy_state = vop_crtc_destroy_state, .enable_vblank = vop_crtc_enable_vblank, .disable_vblank = vop_crtc_disable_vblank, + .set_crc_source = vop_crtc_set_crc_source, }; static void vop_fb_unref_worker(struct drm_flip_work *work, void *val) diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index 8244890e6d53..5fcabc04f307 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -711,13 +711,10 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_connector_register(connector); - if (ret < 0) - goto err_cleanup; ret = shmob_drm_backlight_init(&sdev->connector); if (ret < 0) - goto err_sysfs; + goto err_cleanup; ret = drm_mode_connector_attach_encoder(connector, encoder); if (ret < 0) @@ -731,8 +728,6 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev, err_backlight: shmob_drm_backlight_exit(&sdev->connector); -err_sysfs: - drm_connector_unregister(connector); err_cleanup: drm_connector_cleanup(connector); return ret; diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 34fefa0ba0f0..1c7b318b8998 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -103,100 +103,6 @@ static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev, * DRM operations */ -static void shmob_drm_unload(struct drm_device *dev) -{ - drm_kms_helper_poll_fini(dev); - drm_mode_config_cleanup(dev); - drm_vblank_cleanup(dev); - drm_irq_uninstall(dev); - - dev->dev_private = NULL; -} - -static int shmob_drm_load(struct drm_device *dev, unsigned long flags) -{ - struct shmob_drm_platform_data *pdata = dev->dev->platform_data; - struct platform_device *pdev = dev->platformdev; - struct shmob_drm_device *sdev; - struct resource *res; - unsigned int i; - int ret; - - if (pdata == NULL) { - dev_err(dev->dev, "no platform data\n"); - return -EINVAL; - } - - sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); - if (sdev == NULL) { - dev_err(dev->dev, "failed to allocate private data\n"); - return -ENOMEM; - } - - sdev->dev = &pdev->dev; - sdev->pdata = pdata; - spin_lock_init(&sdev->irq_lock); - - sdev->ddev = dev; - dev->dev_private = sdev; - - /* I/O resources and clocks */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get memory resource\n"); - return -EINVAL; - } - - sdev->mmio = devm_ioremap_nocache(&pdev->dev, res->start, - resource_size(res)); - if (sdev->mmio == NULL) { - dev_err(&pdev->dev, "failed to remap memory resource\n"); - return -ENOMEM; - } - - ret = shmob_drm_setup_clocks(sdev, pdata->clk_source); - if (ret < 0) - return ret; - - ret = shmob_drm_init_interface(sdev); - if (ret < 0) - return ret; - - ret = shmob_drm_modeset_init(sdev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to initialize mode setting\n"); - return ret; - } - - for (i = 0; i < 4; ++i) { - ret = shmob_drm_plane_create(sdev, i); - if (ret < 0) { - dev_err(&pdev->dev, "failed to create plane %u\n", i); - goto done; - } - } - - ret = drm_vblank_init(dev, 1); - if (ret < 0) { - dev_err(&pdev->dev, "failed to initialize vblank\n"); - goto done; - } - - ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); - if (ret < 0) { - dev_err(&pdev->dev, "failed to install IRQ handler\n"); - goto done; - } - - platform_set_drvdata(pdev, sdev); - -done: - if (ret) - shmob_drm_unload(dev); - - return ret; -} - static irqreturn_t shmob_drm_irq(int irq, void *arg) { struct drm_device *dev = arg; @@ -236,8 +142,6 @@ static const struct file_operations shmob_drm_fops = { static struct drm_driver shmob_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME, - .load = shmob_drm_load, - .unload = shmob_drm_unload, .irq_handler = shmob_drm_irq, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, @@ -297,18 +201,116 @@ static const struct dev_pm_ops shmob_drm_pm_ops = { * Platform driver */ -static int shmob_drm_probe(struct platform_device *pdev) +static int shmob_drm_remove(struct platform_device *pdev) { - return drm_platform_init(&shmob_drm_driver, pdev); + struct shmob_drm_device *sdev = platform_get_drvdata(pdev); + struct drm_device *ddev = sdev->ddev; + + drm_dev_unregister(ddev); + drm_kms_helper_poll_fini(ddev); + drm_mode_config_cleanup(ddev); + drm_irq_uninstall(ddev); + drm_dev_unref(ddev); + + return 0; } -static int shmob_drm_remove(struct platform_device *pdev) +static int shmob_drm_probe(struct platform_device *pdev) { - struct shmob_drm_device *sdev = platform_get_drvdata(pdev); + struct shmob_drm_platform_data *pdata = pdev->dev.platform_data; + struct shmob_drm_device *sdev; + struct drm_device *ddev; + struct resource *res; + unsigned int i; + int ret; + + if (pdata == NULL) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } - drm_put_dev(sdev->ddev); + /* + * Allocate and initialize the driver private data, I/O resources and + * clocks. + */ + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); + if (sdev == NULL) + return -ENOMEM; + + sdev->dev = &pdev->dev; + sdev->pdata = pdata; + spin_lock_init(&sdev->irq_lock); + + platform_set_drvdata(pdev, sdev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sdev->mmio = devm_ioremap_resource(&pdev->dev, res); + if (sdev->mmio == NULL) + return -ENOMEM; + + ret = shmob_drm_setup_clocks(sdev, pdata->clk_source); + if (ret < 0) + return ret; + + ret = shmob_drm_init_interface(sdev); + if (ret < 0) + return ret; + + /* Allocate and initialize the DRM device. */ + ddev = drm_dev_alloc(&shmob_drm_driver, &pdev->dev); + if (IS_ERR(ddev)) + return PTR_ERR(ddev); + + sdev->ddev = ddev; + ddev->dev_private = sdev; + + ret = shmob_drm_modeset_init(sdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to initialize mode setting\n"); + goto err_free_drm_dev; + } + + for (i = 0; i < 4; ++i) { + ret = shmob_drm_plane_create(sdev, i); + if (ret < 0) { + dev_err(&pdev->dev, "failed to create plane %u\n", i); + goto err_modeset_cleanup; + } + } + + ret = drm_vblank_init(ddev, 1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to initialize vblank\n"); + goto err_modeset_cleanup; + } + + ret = drm_irq_install(ddev, platform_get_irq(pdev, 0)); + if (ret < 0) { + dev_err(&pdev->dev, "failed to install IRQ handler\n"); + goto err_vblank_cleanup; + } + + /* + * Register the DRM device with the core and the connectors with + * sysfs. + */ + ret = drm_dev_register(ddev, 0); + if (ret < 0) + goto err_irq_uninstall; return 0; + +err_irq_uninstall: + drm_irq_uninstall(ddev); +err_vblank_cleanup: + drm_vblank_cleanup(ddev); +err_modeset_cleanup: + drm_kms_helper_poll_fini(ddev); + drm_mode_config_cleanup(ddev); +err_free_drm_dev: + drm_dev_unref(ddev); + + return ret; } static struct platform_driver shmob_drm_platform_driver = { diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 3b15c2cb2306..6003c664ba0b 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -263,8 +263,6 @@ static int sti_bind(struct device *dev) if (IS_ERR(ddev)) return PTR_ERR(ddev); - ddev->platformdev = to_platform_device(dev); - ret = sti_init(ddev); if (ret) goto err_drm_dev_unref; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 81d80a2ffeb1..5b4ed0ea7768 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -245,7 +245,6 @@ static int tilcdc_init(struct drm_driver *ddrv, struct device *dev) if (IS_ERR(ddev)) return PTR_ERR(ddev); - ddev->platformdev = pdev; ddev->dev_private = priv; platform_set_drvdata(pdev, ddev); drm_mode_config_init(ddev); diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index d59f68936306..93900a83dced 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -178,9 +178,7 @@ struct virtio_gpu_device { struct virtio_gpu_queue ctrlq; struct virtio_gpu_queue cursorq; - struct list_head free_vbufs; - spinlock_t free_vbufs_lock; - void *vbufs; + struct kmem_cache *vbufs; bool vqs_ready; struct idr resource_idr; diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 43ea0dc957d2..472e34986a44 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -74,51 +74,19 @@ void virtio_gpu_cursor_ack(struct virtqueue *vq) int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev) { - struct virtio_gpu_vbuffer *vbuf; - int i, size, count = 16; - void *ptr; - - INIT_LIST_HEAD(&vgdev->free_vbufs); - spin_lock_init(&vgdev->free_vbufs_lock); - count += virtqueue_get_vring_size(vgdev->ctrlq.vq); - count += virtqueue_get_vring_size(vgdev->cursorq.vq); - size = count * VBUFFER_SIZE; - DRM_INFO("virtio vbuffers: %d bufs, %zdB each, %dkB total.\n", - count, VBUFFER_SIZE, size / 1024); - - vgdev->vbufs = kzalloc(size, GFP_KERNEL); + vgdev->vbufs = kmem_cache_create("virtio-gpu-vbufs", + VBUFFER_SIZE, + __alignof__(struct virtio_gpu_vbuffer), + 0, NULL); if (!vgdev->vbufs) return -ENOMEM; - - for (i = 0, ptr = vgdev->vbufs; - i < count; - i++, ptr += VBUFFER_SIZE) { - vbuf = ptr; - list_add(&vbuf->list, &vgdev->free_vbufs); - } return 0; } void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev) { - struct virtio_gpu_vbuffer *vbuf; - int i, count = 0; - - count += virtqueue_get_vring_size(vgdev->ctrlq.vq); - count += virtqueue_get_vring_size(vgdev->cursorq.vq); - - spin_lock(&vgdev->free_vbufs_lock); - for (i = 0; i < count; i++) { - if (WARN_ON(list_empty(&vgdev->free_vbufs))) { - spin_unlock(&vgdev->free_vbufs_lock); - return; - } - vbuf = list_first_entry(&vgdev->free_vbufs, - struct virtio_gpu_vbuffer, list); - list_del(&vbuf->list); - } - spin_unlock(&vgdev->free_vbufs_lock); - kfree(vgdev->vbufs); + kmem_cache_destroy(vgdev->vbufs); + vgdev->vbufs = NULL; } static struct virtio_gpu_vbuffer* @@ -128,12 +96,9 @@ virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev, { struct virtio_gpu_vbuffer *vbuf; - spin_lock(&vgdev->free_vbufs_lock); - BUG_ON(list_empty(&vgdev->free_vbufs)); - vbuf = list_first_entry(&vgdev->free_vbufs, - struct virtio_gpu_vbuffer, list); - list_del(&vbuf->list); - spin_unlock(&vgdev->free_vbufs_lock); + vbuf = kmem_cache_alloc(vgdev->vbufs, GFP_KERNEL); + if (IS_ERR(vbuf)) + return ERR_CAST(vbuf); memset(vbuf, 0, VBUFFER_SIZE); BUG_ON(size > MAX_INLINE_CMD_SIZE); @@ -208,9 +173,7 @@ static void free_vbuf(struct virtio_gpu_device *vgdev, if (vbuf->resp_size > MAX_INLINE_RESP_SIZE) kfree(vbuf->resp_buf); kfree(vbuf->data_buf); - spin_lock(&vgdev->free_vbufs_lock); - list_add(&vbuf->list, &vgdev->free_vbufs); - spin_unlock(&vgdev->free_vbufs_lock); + kmem_cache_free(vgdev->vbufs, vbuf); } static void reclaim_vbufs(struct virtqueue *vq, struct list_head *reclaim_list) diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 5f961416c4ee..1ab9bceee755 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \ - ipu-smfc.o ipu-vdi.o + ipu-pre.o ipu-prg.o ipu-smfc.o ipu-vdi.o diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index 8368e6f766ee..7aefccec31b1 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -51,15 +51,17 @@ int ipu_get_num(struct ipu_soc *ipu) } EXPORT_SYMBOL_GPL(ipu_get_num); -void ipu_srm_dp_sync_update(struct ipu_soc *ipu) +void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) { u32 val; val = ipu_cm_read(ipu, IPU_SRM_PRI2); - val |= 0x8; + val &= ~DP_S_SRM_MODE_MASK; + val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : + DP_S_SRM_MODE_NOW; ipu_cm_write(ipu, val, IPU_SRM_PRI2); } -EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update); +EXPORT_SYMBOL_GPL(ipu_srm_dp_update); enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) { @@ -81,6 +83,12 @@ enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) case DRM_FORMAT_ABGR8888: case DRM_FORMAT_RGBA8888: case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_RGB565_A8: + case DRM_FORMAT_BGR565_A8: + case DRM_FORMAT_RGB888_A8: + case DRM_FORMAT_BGR888_A8: + case DRM_FORMAT_RGBX8888_A8: + case DRM_FORMAT_BGRX8888_A8: return IPUV3_COLORSPACE_RGB; case DRM_FORMAT_YUYV: case DRM_FORMAT_UYVY: @@ -931,6 +939,7 @@ static const struct of_device_id imx_ipu_dt_ids[] = { { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, + { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); @@ -1390,11 +1399,19 @@ static int ipu_probe(struct platform_device *pdev) if (!ipu) return -ENODEV; + ipu->id = of_alias_get_id(np, "ipu"); + + if (of_device_is_compatible(np, "fsl,imx6qp-ipu")) { + ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, + "fsl,prg", ipu->id); + if (!ipu->prg_priv) + return -EPROBE_DEFER; + } + for (i = 0; i < 64; i++) ipu->channel[i].ipu = ipu; ipu->devtype = devtype; ipu->ipu_type = devtype->type; - ipu->id = of_alias_get_id(np, "ipu"); spin_lock_init(&ipu->lock); mutex_init(&ipu->channel_lock); @@ -1520,7 +1537,23 @@ static struct platform_driver imx_ipu_driver = { .remove = ipu_remove, }; -module_platform_driver(imx_ipu_driver); +static struct platform_driver * const drivers[] = { + &ipu_pre_drv, + &ipu_prg_drv, + &imx_ipu_driver, +}; + +static int __init imx_ipu_init(void) +{ + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); +} +module_init(imx_ipu_init); + +static void __exit imx_ipu_exit(void) +{ + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); +} +module_exit(imx_ipu_exit); MODULE_ALIAS("platform:imx-ipuv3"); MODULE_DESCRIPTION("i.MX IPU v3 driver"); diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 4b2b67113d92..114160dfc3ad 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -537,6 +537,43 @@ static const struct ipu_rgb def_bgra_16 = { #define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ (pix->width * y) + (x)) +#define NUM_ALPHA_CHANNELS 7 + +/* See Table 37-12. Alpha channels mapping. */ +static int ipu_channel_albm(int ch_num) +{ + switch (ch_num) { + case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: return 0; + case IPUV3_CHANNEL_G_MEM_IC_PP: return 1; + case IPUV3_CHANNEL_MEM_FG_SYNC: return 2; + case IPUV3_CHANNEL_MEM_FG_ASYNC: return 3; + case IPUV3_CHANNEL_MEM_BG_SYNC: return 4; + case IPUV3_CHANNEL_MEM_BG_ASYNC: return 5; + case IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB: return 6; + default: + return -EINVAL; + } +} + +static void ipu_cpmem_set_separate_alpha(struct ipuv3_channel *ch) +{ + struct ipu_soc *ipu = ch->ipu; + int albm; + u32 val; + + albm = ipu_channel_albm(ch->num); + if (albm < 0) + return; + + ipu_ch_param_write_field(ch, IPU_FIELD_ALU, 1); + ipu_ch_param_write_field(ch, IPU_FIELD_ALBM, albm); + ipu_ch_param_write_field(ch, IPU_FIELD_CRE, 1); + + val = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); + val |= BIT(ch->num); + ipu_idmac_write(ipu, val, IDMAC_SEP_ALPHA); +} + int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc) { switch (drm_fourcc) { @@ -599,22 +636,28 @@ int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc) break; case DRM_FORMAT_RGBA8888: case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBX8888_A8: ipu_cpmem_set_format_rgb(ch, &def_rgbx_32); break; case DRM_FORMAT_BGRA8888: case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_BGRX8888_A8: ipu_cpmem_set_format_rgb(ch, &def_bgrx_32); break; case DRM_FORMAT_BGR888: + case DRM_FORMAT_BGR888_A8: ipu_cpmem_set_format_rgb(ch, &def_bgr_24); break; case DRM_FORMAT_RGB888: + case DRM_FORMAT_RGB888_A8: ipu_cpmem_set_format_rgb(ch, &def_rgb_24); break; case DRM_FORMAT_RGB565: + case DRM_FORMAT_RGB565_A8: ipu_cpmem_set_format_rgb(ch, &def_rgb_16); break; case DRM_FORMAT_BGR565: + case DRM_FORMAT_BGR565_A8: ipu_cpmem_set_format_rgb(ch, &def_bgr_16); break; case DRM_FORMAT_ARGB1555: @@ -636,6 +679,20 @@ int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc) return -EINVAL; } + switch (drm_fourcc) { + case DRM_FORMAT_RGB565_A8: + case DRM_FORMAT_BGR565_A8: + case DRM_FORMAT_RGB888_A8: + case DRM_FORMAT_BGR888_A8: + case DRM_FORMAT_RGBX8888_A8: + case DRM_FORMAT_BGRX8888_A8: + ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); + ipu_cpmem_set_separate_alpha(ch); + break; + default: + break; + } + return 0; } EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt); @@ -644,6 +701,7 @@ int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image) { struct v4l2_pix_format *pix = &image->pix; int offset, u_offset, v_offset; + int ret = 0; pr_debug("%s: resolution: %dx%d stride: %d\n", __func__, pix->width, pix->height, @@ -719,14 +777,30 @@ int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image) offset = image->rect.left * 3 + image->rect.top * pix->bytesperline; break; + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + offset = image->rect.left + image->rect.top * pix->bytesperline; + break; + case V4L2_PIX_FMT_SBGGR16: + case V4L2_PIX_FMT_SGBRG16: + case V4L2_PIX_FMT_SGRBG16: + case V4L2_PIX_FMT_SRGGB16: + offset = image->rect.left * 2 + + image->rect.top * pix->bytesperline; + break; default: - return -EINVAL; + /* This should not happen */ + WARN_ON(1); + offset = 0; + ret = -EINVAL; } ipu_cpmem_set_buffer(ch, 0, image->phys0 + offset); ipu_cpmem_set_buffer(ch, 1, image->phys1 + offset); - return 0; + return ret; } EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c index 659475c1e44a..7a4b8362dda8 100644 --- a/drivers/gpu/ipu-v3/ipu-dc.c +++ b/drivers/gpu/ipu-v3/ipu-dc.c @@ -112,8 +112,6 @@ struct ipu_dc_priv { struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; struct mutex mutex; struct completion comp; - int dc_irq; - int dp_irq; int use_count; }; @@ -262,47 +260,13 @@ void ipu_dc_enable_channel(struct ipu_dc *dc) } EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); -static irqreturn_t dc_irq_handler(int irq, void *dev_id) -{ - struct ipu_dc *dc = dev_id; - u32 reg; - - reg = readl(dc->base + DC_WR_CH_CONF); - reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; - writel(reg, dc->base + DC_WR_CH_CONF); - - /* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */ - - complete(&dc->priv->comp); - return IRQ_HANDLED; -} - void ipu_dc_disable_channel(struct ipu_dc *dc) { - struct ipu_dc_priv *priv = dc->priv; - int irq; - unsigned long ret; u32 val; - /* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */ - if (dc->chno == 1) - irq = priv->dc_irq; - else if (dc->chno == 5) - irq = priv->dp_irq; - else - return; - - init_completion(&priv->comp); - enable_irq(irq); - ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50)); - disable_irq(irq); - if (ret == 0) { - dev_warn(priv->dev, "DC stop timeout after 50 ms\n"); - - val = readl(dc->base + DC_WR_CH_CONF); - val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; - writel(val, dc->base + DC_WR_CH_CONF); - } + val = readl(dc->base + DC_WR_CH_CONF); + val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + writel(val, dc->base + DC_WR_CH_CONF); } EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); @@ -389,7 +353,7 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, struct ipu_dc_priv *priv; static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, 0x78, 0, 0x94, 0xb4}; - int i, ret; + int i; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -410,23 +374,6 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, priv->channels[i].base = priv->dc_reg + channel_offsets[i]; } - priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1); - if (!priv->dc_irq) - return -EINVAL; - ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL, - &priv->channels[1]); - if (ret < 0) - return ret; - disable_irq(priv->dc_irq); - priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END); - if (!priv->dp_irq) - return -EINVAL; - ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL, - &priv->channels[5]); - if (ret < 0) - return ret; - disable_irq(priv->dp_irq); - writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | DC_WR_CH_CONF_PROG_DI_ID, priv->channels[1].base + DC_WR_CH_CONF); diff --git a/drivers/gpu/ipu-v3/ipu-dp.c b/drivers/gpu/ipu-v3/ipu-dp.c index 98686edbcdbb..9b2b3fa479c4 100644 --- a/drivers/gpu/ipu-v3/ipu-dp.c +++ b/drivers/gpu/ipu-v3/ipu-dp.c @@ -112,7 +112,7 @@ int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); } - ipu_srm_dp_sync_update(priv->ipu); + ipu_srm_dp_update(priv->ipu, true); mutex_unlock(&priv->mutex); @@ -127,7 +127,7 @@ int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos) writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS); - ipu_srm_dp_sync_update(priv->ipu); + ipu_srm_dp_update(priv->ipu, true); return 0; } @@ -207,7 +207,7 @@ int ipu_dp_setup_channel(struct ipu_dp *dp, flow->out_cs, DP_COM_CONF_CSC_DEF_FG); } - ipu_srm_dp_sync_update(priv->ipu); + ipu_srm_dp_update(priv->ipu, true); mutex_unlock(&priv->mutex); @@ -247,7 +247,7 @@ int ipu_dp_enable_channel(struct ipu_dp *dp) reg |= DP_COM_CONF_FG_EN; writel(reg, flow->base + DP_COM_CONF); - ipu_srm_dp_sync_update(priv->ipu); + ipu_srm_dp_update(priv->ipu, true); mutex_unlock(&priv->mutex); @@ -255,7 +255,7 @@ int ipu_dp_enable_channel(struct ipu_dp *dp) } EXPORT_SYMBOL_GPL(ipu_dp_enable_channel); -void ipu_dp_disable_channel(struct ipu_dp *dp) +void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync) { struct ipu_flow *flow = to_flow(dp); struct ipu_dp_priv *priv = flow->priv; @@ -275,10 +275,7 @@ void ipu_dp_disable_channel(struct ipu_dp *dp) writel(reg, flow->base + DP_COM_CONF); writel(0, flow->base + DP_FG_POS); - ipu_srm_dp_sync_update(priv->ipu); - - if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC)) - ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50); + ipu_srm_dp_update(priv->ipu, sync); mutex_unlock(&priv->mutex); } diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index 805b6fa7b5f4..524a717ab28e 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -671,7 +671,12 @@ static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, ipu_ic_task_idma_init(chan->ic, channel, width, height, burst_size, rot_mode); - ipu_cpmem_set_axi_id(channel, 1); + /* + * Setting a non-zero AXI ID collides with the PRG AXI snooping, so + * only do this when there is no PRG present. + */ + if (!channel->ipu->prg_priv) + ipu_cpmem_set_axi_id(channel, 1); ipu_idmac_set_double_buffer(channel, ctx->double_buffering); } diff --git a/drivers/gpu/ipu-v3/ipu-pre.c b/drivers/gpu/ipu-v3/ipu-pre.c new file mode 100644 index 000000000000..c55563379e2e --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-pre.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2017 Lucas Stach, Pengutronix + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <drm/drm_fourcc.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/genalloc.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <video/imx-ipu-v3.h> + +#include "ipu-prv.h" + +#define IPU_PRE_MAX_WIDTH 2048 +#define IPU_PRE_NUM_SCANLINES 8 + +#define IPU_PRE_CTRL 0x000 +#define IPU_PRE_CTRL_SET 0x004 +#define IPU_PRE_CTRL_ENABLE (1 << 0) +#define IPU_PRE_CTRL_BLOCK_EN (1 << 1) +#define IPU_PRE_CTRL_BLOCK_16 (1 << 2) +#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4) +#define IPU_PRE_CTRL_VFLIP (1 << 5) +#define IPU_PRE_CTRL_SO (1 << 6) +#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) +#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) +#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) +#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) +#define IPU_PRE_CTRL_EN_REPEAT (1 << 28) +#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) +#define IPU_PRE_CTRL_CLKGATE (1 << 30) +#define IPU_PRE_CTRL_SFTRST (1 << 31) + +#define IPU_PRE_CUR_BUF 0x030 + +#define IPU_PRE_NEXT_BUF 0x040 + +#define IPU_PRE_TPR_CTRL 0x070 +#define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0) +#define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff + +#define IPU_PRE_PREFETCH_ENG_CTRL 0x080 +#define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0) +#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) +#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) +#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) +#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) +#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) +#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) +#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15) + +#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0 +#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) +#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16) + +#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 +#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) +#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16) + +#define IPU_PRE_STORE_ENG_CTRL 0x110 +#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) +#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) +#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) + +#define IPU_PRE_STORE_ENG_SIZE 0x130 +#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) +#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16) + +#define IPU_PRE_STORE_ENG_PITCH 0x140 +#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0) + +#define IPU_PRE_STORE_ENG_ADDR 0x150 + +struct ipu_pre { + struct list_head list; + struct device *dev; + + void __iomem *regs; + struct clk *clk_axi; + struct gen_pool *iram; + + dma_addr_t buffer_paddr; + void *buffer_virt; + bool in_use; +}; + +static DEFINE_MUTEX(ipu_pre_list_mutex); +static LIST_HEAD(ipu_pre_list); +static int available_pres; + +int ipu_pre_get_available_count(void) +{ + return available_pres; +} + +struct ipu_pre * +ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index) +{ + struct device_node *pre_node = of_parse_phandle(dev->of_node, + name, index); + struct ipu_pre *pre; + + mutex_lock(&ipu_pre_list_mutex); + list_for_each_entry(pre, &ipu_pre_list, list) { + if (pre_node == pre->dev->of_node) { + mutex_unlock(&ipu_pre_list_mutex); + device_link_add(dev, pre->dev, DL_FLAG_AUTOREMOVE); + return pre; + } + } + mutex_unlock(&ipu_pre_list_mutex); + + return NULL; +} + +int ipu_pre_get(struct ipu_pre *pre) +{ + u32 val; + + if (pre->in_use) + return -EBUSY; + + clk_prepare_enable(pre->clk_axi); + + /* first get the engine out of reset and remove clock gating */ + writel(0, pre->regs + IPU_PRE_CTRL); + + /* init defaults that should be applied to all streams */ + val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN | + IPU_PRE_CTRL_HANDSHAKE_EN | + IPU_PRE_CTRL_TPR_REST_SEL | + IPU_PRE_CTRL_BLOCK_16 | IPU_PRE_CTRL_SDW_UPDATE; + writel(val, pre->regs + IPU_PRE_CTRL); + + pre->in_use = true; + return 0; +} + +void ipu_pre_put(struct ipu_pre *pre) +{ + u32 val; + + val = IPU_PRE_CTRL_SFTRST | IPU_PRE_CTRL_CLKGATE; + writel(val, pre->regs + IPU_PRE_CTRL); + + clk_disable_unprepare(pre->clk_axi); + + pre->in_use = false; +} + +void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, + unsigned int height, unsigned int stride, u32 format, + unsigned int bufaddr) +{ + const struct drm_format_info *info = drm_format_info(format); + u32 active_bpp = info->cpp[0] >> 1; + u32 val; + + writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF); + writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); + + val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) | + IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) | + IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) | + IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS | + IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN; + writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL); + + val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) | + IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height); + writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE); + + val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride); + writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH); + + val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) | + IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) | + IPU_PRE_STORE_ENG_CTRL_STORE_EN; + writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL); + + val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) | + IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height); + writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE); + + val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride); + writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH); + + writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR); + + val = readl(pre->regs + IPU_PRE_CTRL); + val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE | + IPU_PRE_CTRL_SDW_UPDATE; + writel(val, pre->regs + IPU_PRE_CTRL); +} + +void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) +{ + writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); + writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET); +} + +u32 ipu_pre_get_baddr(struct ipu_pre *pre) +{ + return (u32)pre->buffer_paddr; +} + +static int ipu_pre_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct ipu_pre *pre; + + pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL); + if (!pre) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pre->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pre->regs)) + return PTR_ERR(pre->regs); + + pre->clk_axi = devm_clk_get(dev, "axi"); + if (IS_ERR(pre->clk_axi)) + return PTR_ERR(pre->clk_axi); + + pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0); + if (!pre->iram) + return -EPROBE_DEFER; + + /* + * Allocate IRAM buffer with maximum size. This could be made dynamic, + * but as there is no other user of this IRAM region and we can fit all + * max sized buffers into it, there is no need yet. + */ + pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH * + IPU_PRE_NUM_SCANLINES * 4, + &pre->buffer_paddr); + if (!pre->buffer_virt) + return -ENOMEM; + + pre->dev = dev; + platform_set_drvdata(pdev, pre); + mutex_lock(&ipu_pre_list_mutex); + list_add(&pre->list, &ipu_pre_list); + available_pres++; + mutex_unlock(&ipu_pre_list_mutex); + + return 0; +} + +static int ipu_pre_remove(struct platform_device *pdev) +{ + struct ipu_pre *pre = platform_get_drvdata(pdev); + + mutex_lock(&ipu_pre_list_mutex); + list_del(&pre->list); + available_pres--; + mutex_unlock(&ipu_pre_list_mutex); + + if (pre->buffer_virt) + gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt, + IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4); + return 0; +} + +static const struct of_device_id ipu_pre_dt_ids[] = { + { .compatible = "fsl,imx6qp-pre", }, + { /* sentinel */ }, +}; + +struct platform_driver ipu_pre_drv = { + .probe = ipu_pre_probe, + .remove = ipu_pre_remove, + .driver = { + .name = "imx-ipu-pre", + .of_match_table = ipu_pre_dt_ids, + }, +}; diff --git a/drivers/gpu/ipu-v3/ipu-prg.c b/drivers/gpu/ipu-v3/ipu-prg.c new file mode 100644 index 000000000000..caca57febbd6 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-prg.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2016-2017 Lucas Stach, Pengutronix + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <drm/drm_fourcc.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <video/imx-ipu-v3.h> + +#include "ipu-prv.h" + +#define IPU_PRG_CTL 0x00 +#define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i)) +#define IPU_PRG_CTL_SOFT_ARID_MASK 0x3 +#define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2) +#define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i)) +#define IPU_PRG_CTL_SO(i) (1 << (16 + i)) +#define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i)) +#define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i)) +#define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i)) +#define IPU_PRG_CTL_SOFTRST (1 << 30) +#define IPU_PRG_CTL_SHADOW_EN (1 << 31) + +#define IPU_PRG_STATUS 0x04 +#define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2)) +#define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2)) + +#define IPU_PRG_QOS 0x08 +#define IPU_PRG_QOS_ARID_MASK 0xf +#define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4) + +#define IPU_PRG_REG_UPDATE 0x0c +#define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0) + +#define IPU_PRG_STRIDE(i) (0x10 + i * 0x4) +#define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff + +#define IPU_PRG_CROP_LINE 0x1c + +#define IPU_PRG_THD 0x20 + +#define IPU_PRG_BADDR(i) (0x24 + i * 0x4) + +#define IPU_PRG_OFFSET(i) (0x30 + i * 0x4) + +#define IPU_PRG_ILO(i) (0x3c + i * 0x4) + +#define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4) +#define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff +#define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0 +#define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff +#define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16 + +struct ipu_prg_channel { + bool enabled; + int used_pre; +}; + +struct ipu_prg { + struct list_head list; + struct device *dev; + int id; + + void __iomem *regs; + struct clk *clk_ipg, *clk_axi; + struct regmap *iomuxc_gpr; + struct ipu_pre *pres[3]; + + struct ipu_prg_channel chan[3]; +}; + +static DEFINE_MUTEX(ipu_prg_list_mutex); +static LIST_HEAD(ipu_prg_list); + +struct ipu_prg * +ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id) +{ + struct device_node *prg_node = of_parse_phandle(dev->of_node, + name, 0); + struct ipu_prg *prg; + + mutex_lock(&ipu_prg_list_mutex); + list_for_each_entry(prg, &ipu_prg_list, list) { + if (prg_node == prg->dev->of_node) { + mutex_unlock(&ipu_prg_list_mutex); + device_link_add(dev, prg->dev, DL_FLAG_AUTOREMOVE); + prg->id = ipu_id; + return prg; + } + } + mutex_unlock(&ipu_prg_list_mutex); + + return NULL; +} + +int ipu_prg_max_active_channels(void) +{ + return ipu_pre_get_available_count(); +} +EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels); + +bool ipu_prg_present(struct ipu_soc *ipu) +{ + if (ipu->prg_priv) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(ipu_prg_present); + +bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, + uint64_t modifier) +{ + const struct drm_format_info *info = drm_format_info(format); + + if (info->num_planes != 1) + return false; + + return true; +} +EXPORT_SYMBOL_GPL(ipu_prg_format_supported); + +int ipu_prg_enable(struct ipu_soc *ipu) +{ + struct ipu_prg *prg = ipu->prg_priv; + int ret; + + if (!prg) + return 0; + + ret = clk_prepare_enable(prg->clk_axi); + if (ret) + goto fail_disable_ipg; + + return 0; + +fail_disable_ipg: + clk_disable_unprepare(prg->clk_ipg); + + return ret; +} +EXPORT_SYMBOL_GPL(ipu_prg_enable); + +void ipu_prg_disable(struct ipu_soc *ipu) +{ + struct ipu_prg *prg = ipu->prg_priv; + + if (!prg) + return; + + clk_disable_unprepare(prg->clk_axi); +} +EXPORT_SYMBOL_GPL(ipu_prg_disable); + +/* + * The channel configuartion functions below are not thread safe, as they + * must be only called from the atomic commit path in the DRM driver, which + * is properly serialized. + */ +static int ipu_prg_ipu_to_prg_chan(int ipu_chan) +{ + /* + * This isn't clearly documented in the RM, but IPU to PRG channel + * assignment is fixed, as only with this mapping the control signals + * match up. + */ + switch (ipu_chan) { + case IPUV3_CHANNEL_MEM_BG_SYNC: + return 0; + case IPUV3_CHANNEL_MEM_FG_SYNC: + return 1; + case IPUV3_CHANNEL_MEM_DC_SYNC: + return 2; + default: + return -EINVAL; + } +} + +static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan) +{ + int i, ret; + + /* channel 0 is special as it is hardwired to one of the PREs */ + if (prg_chan == 0) { + ret = ipu_pre_get(prg->pres[0]); + if (ret) + goto fail; + prg->chan[prg_chan].used_pre = 0; + return 0; + } + + for (i = 1; i < 3; i++) { + ret = ipu_pre_get(prg->pres[i]); + if (!ret) { + u32 val, mux; + int shift; + + prg->chan[prg_chan].used_pre = i; + + /* configure the PRE to PRG channel mux */ + shift = (i == 1) ? 12 : 14; + mux = (prg->id << 1) | (prg_chan - 1); + regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, + 0x3 << shift, mux << shift); + + /* check other mux, must not point to same channel */ + shift = (i == 1) ? 14 : 12; + regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val); + if (((val >> shift) & 0x3) == mux) { + regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, + 0x3 << shift, + (mux ^ 0x1) << shift); + } + + return 0; + } + } + +fail: + dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan); + return ret; +} + +static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan) +{ + struct ipu_prg_channel *chan = &prg->chan[prg_chan]; + + ipu_pre_put(prg->pres[chan->used_pre]); + chan->used_pre = -1; +} + +void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) +{ + int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); + struct ipu_prg *prg = ipu_chan->ipu->prg_priv; + struct ipu_prg_channel *chan = &prg->chan[prg_chan]; + u32 val; + + if (!chan->enabled || prg_chan < 0) + return; + + clk_prepare_enable(prg->clk_ipg); + + val = readl(prg->regs + IPU_PRG_CTL); + val |= IPU_PRG_CTL_BYPASS(prg_chan); + writel(val, prg->regs + IPU_PRG_CTL); + + val = IPU_PRG_REG_UPDATE_REG_UPDATE; + writel(val, prg->regs + IPU_PRG_REG_UPDATE); + + clk_disable_unprepare(prg->clk_ipg); + + ipu_prg_put_pre(prg, prg_chan); + + chan->enabled = false; +} +EXPORT_SYMBOL_GPL(ipu_prg_channel_disable); + +int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, + unsigned int axi_id, unsigned int width, + unsigned int height, unsigned int stride, + u32 format, unsigned long *eba) +{ + int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); + struct ipu_prg *prg = ipu_chan->ipu->prg_priv; + struct ipu_prg_channel *chan = &prg->chan[prg_chan]; + u32 val; + int ret; + + if (prg_chan < 0) + return prg_chan; + + if (chan->enabled) { + ipu_pre_update(prg->pres[chan->used_pre], *eba); + return 0; + } + + ret = ipu_prg_get_pre(prg, prg_chan); + if (ret) + return ret; + + ipu_pre_configure(prg->pres[chan->used_pre], + width, height, stride, format, *eba); + + + ret = clk_prepare_enable(prg->clk_ipg); + if (ret) { + ipu_prg_put_pre(prg, prg_chan); + return ret; + } + + val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK; + writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan)); + + val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) << + IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) | + ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) << + IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT); + writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan)); + + val = ipu_pre_get_baddr(prg->pres[chan->used_pre]); + *eba = val; + writel(val, prg->regs + IPU_PRG_BADDR(prg_chan)); + + val = readl(prg->regs + IPU_PRG_CTL); + /* counter load enable */ + val |= IPU_PRG_CTL_CNT_LOAD_EN(prg_chan); + /* config AXI ID */ + val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK << + IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan)); + val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id); + /* enable channel */ + val &= ~IPU_PRG_CTL_BYPASS(prg_chan); + writel(val, prg->regs + IPU_PRG_CTL); + + val = IPU_PRG_REG_UPDATE_REG_UPDATE; + writel(val, prg->regs + IPU_PRG_REG_UPDATE); + + clk_disable_unprepare(prg->clk_ipg); + + chan->enabled = true; + return 0; +} +EXPORT_SYMBOL_GPL(ipu_prg_channel_configure); + +static int ipu_prg_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct ipu_prg *prg; + u32 val; + int i, ret; + + prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL); + if (!prg) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + prg->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(prg->regs)) + return PTR_ERR(prg->regs); + + + prg->clk_ipg = devm_clk_get(dev, "ipg"); + if (IS_ERR(prg->clk_ipg)) + return PTR_ERR(prg->clk_ipg); + + prg->clk_axi = devm_clk_get(dev, "axi"); + if (IS_ERR(prg->clk_axi)) + return PTR_ERR(prg->clk_axi); + + prg->iomuxc_gpr = + syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(prg->iomuxc_gpr)) + return PTR_ERR(prg->iomuxc_gpr); + + for (i = 0; i < 3; i++) { + prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i); + if (!prg->pres[i]) + return -EPROBE_DEFER; + } + + ret = clk_prepare_enable(prg->clk_ipg); + if (ret) + return ret; + + /* init to free running mode */ + val = readl(prg->regs + IPU_PRG_CTL); + val |= IPU_PRG_CTL_SHADOW_EN; + writel(val, prg->regs + IPU_PRG_CTL); + + /* disable address threshold */ + writel(0xffffffff, prg->regs + IPU_PRG_THD); + + clk_disable_unprepare(prg->clk_ipg); + + prg->dev = dev; + platform_set_drvdata(pdev, prg); + mutex_lock(&ipu_prg_list_mutex); + list_add(&prg->list, &ipu_prg_list); + mutex_unlock(&ipu_prg_list_mutex); + + return 0; +} + +static int ipu_prg_remove(struct platform_device *pdev) +{ + struct ipu_prg *prg = platform_get_drvdata(pdev); + + mutex_lock(&ipu_prg_list_mutex); + list_del(&prg->list); + mutex_unlock(&ipu_prg_list_mutex); + + return 0; +} + +static const struct of_device_id ipu_prg_dt_ids[] = { + { .compatible = "fsl,imx6qp-prg", }, + { /* sentinel */ }, +}; + +struct platform_driver ipu_prg_drv = { + .probe = ipu_prg_probe, + .remove = ipu_prg_remove, + .driver = { + .name = "imx-ipu-prg", + .of_match_table = ipu_prg_dt_ids, + }, +}; diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h index 22e47b68b14a..ca2a223a0d1e 100644 --- a/drivers/gpu/ipu-v3/ipu-prv.h +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -75,6 +75,11 @@ struct ipu_soc; #define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n)) #define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n)) +/* SRM_PRI2 */ +#define DP_S_SRM_MODE_MASK (0x3 << 3) +#define DP_S_SRM_MODE_NOW (0x3 << 3) +#define DP_S_SRM_MODE_NEXT_FRAME (0x1 << 3) + /* FS_PROC_FLOW1 */ #define FS_PRPENC_ROT_SRC_SEL_MASK (0xf << 0) #define FS_PRPENC_ROT_SRC_SEL_ENC (0x7 << 0) @@ -168,6 +173,8 @@ struct ipu_ic_priv; struct ipu_vdi; struct ipu_image_convert_priv; struct ipu_smfc_priv; +struct ipu_pre; +struct ipu_prg; struct ipu_devtype; @@ -202,6 +209,7 @@ struct ipu_soc { struct ipu_vdi *vdi_priv; struct ipu_image_convert_priv *image_convert_priv; struct ipu_smfc_priv *smfc_priv; + struct ipu_prg *prg_priv; }; static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) @@ -215,7 +223,7 @@ static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, writel(value, ipu->idmac_reg + offset); } -void ipu_srm_dp_sync_update(struct ipu_soc *ipu); +void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync); int ipu_module_enable(struct ipu_soc *ipu, u32 mask); int ipu_module_disable(struct ipu_soc *ipu, u32 mask); @@ -259,4 +267,21 @@ void ipu_cpmem_exit(struct ipu_soc *ipu); int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); void ipu_smfc_exit(struct ipu_soc *ipu); +struct ipu_pre *ipu_pre_lookup_by_phandle(struct device *dev, const char *name, + int index); +int ipu_pre_get_available_count(void); +int ipu_pre_get(struct ipu_pre *pre); +void ipu_pre_put(struct ipu_pre *pre); +u32 ipu_pre_get_baddr(struct ipu_pre *pre); +void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, + unsigned int height, + unsigned int stride, u32 format, unsigned int bufaddr); +void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr); + +struct ipu_prg *ipu_prg_lookup_by_phandle(struct device *dev, const char *name, + int ipu_id); + +extern struct platform_driver ipu_pre_drv; +extern struct platform_driver ipu_prg_drv; + #endif /* __IPU_PRV_H__ */ diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index f6f0c062205c..c99d6eaef1ac 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -49,4 +49,7 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, struct analogix_dp_plat_data *plat_data); void analogix_dp_unbind(struct device *dev, struct device *master, void *data); +int analogix_dp_start_crc(struct drm_connector *connector); +int analogix_dp_stop_crc(struct drm_connector *connector); + #endif /* _ANALOGIX_DP_H_ */ diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index b080a171a23f..bcceee8114a4 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -21,12 +21,6 @@ enum { DW_HDMI_RES_MAX, }; -enum dw_hdmi_devtype { - IMX6Q_HDMI, - IMX6DL_HDMI, - RK3288_HDMI, -}; - enum dw_hdmi_phy_type { DW_HDMI_PHY_DWC_HDMI_TX_PHY = 0x00, DW_HDMI_PHY_DWC_MHL_PHY_HEAC = 0xb2, @@ -57,13 +51,30 @@ struct dw_hdmi_phy_config { u16 vlev_ctr; /* voltage level control */ }; +struct dw_hdmi_phy_ops { + int (*init)(struct dw_hdmi *hdmi, void *data, + struct drm_display_mode *mode); + void (*disable)(struct dw_hdmi *hdmi, void *data); + enum drm_connector_status (*read_hpd)(struct dw_hdmi *hdmi, void *data); +}; + struct dw_hdmi_plat_data { - enum dw_hdmi_devtype dev_type; + struct regmap *regm; + enum drm_mode_status (*mode_valid)(struct drm_connector *connector, + struct drm_display_mode *mode); + + /* Vendor PHY support */ + const struct dw_hdmi_phy_ops *phy_ops; + const char *phy_name; + void *phy_data; + + /* Synopsys PHY support */ const struct dw_hdmi_mpll_config *mpll_cfg; const struct dw_hdmi_curr_ctrl *cur_ctr; const struct dw_hdmi_phy_config *phy_config; - enum drm_mode_status (*mode_valid)(struct drm_connector *connector, - struct drm_display_mode *mode); + int (*configure_phy)(struct dw_hdmi *hdmi, + const struct dw_hdmi_plat_data *pdata, + unsigned long mpixelclock); }; int dw_hdmi_probe(struct platform_device *pdev, @@ -77,4 +88,8 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); void dw_hdmi_audio_enable(struct dw_hdmi *hdmi); void dw_hdmi_audio_disable(struct dw_hdmi *hdmi); +/* PHY configuration */ +void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, + unsigned char addr); + #endif /* __IMX_HDMI_H__ */ diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 6105c050d7bc..0e383438f793 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -47,17 +47,16 @@ #include <linux/miscdevice.h> #include <linux/mm.h> #include <linux/mutex.h> -#include <linux/pci.h> #include <linux/platform_device.h> #include <linux/poll.h> #include <linux/ratelimit.h> -#include <linux/rbtree.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/vmalloc.h> #include <linux/workqueue.h> #include <linux/dma-fence.h> +#include <linux/module.h> #include <asm/mman.h> #include <asm/pgalloc.h> @@ -75,26 +74,30 @@ #include <drm/drm_mm.h> #include <drm/drm_os_linux.h> #include <drm/drm_sarea.h> -#include <drm/drm_vma_manager.h> #include <drm/drm_drv.h> +#include <drm/drm_prime.h> +#include <drm/drm_pci.h> +#include <drm/drm_file.h> struct module; -struct drm_file; struct drm_device; struct drm_agp_head; struct drm_local_map; struct drm_device_dma; -struct drm_dma_handle; struct drm_gem_object; struct drm_master; struct drm_vblank_crtc; +struct drm_vma_offset_manager; struct device_node; struct videomode; struct reservation_object; struct dma_buf_attachment; +struct pci_dev; +struct pci_controller; + /* * The following categories are defined: * @@ -357,97 +360,6 @@ struct drm_ioctl_desc { .name = #ioctl \ } -/* Event queued up for userspace to read */ -struct drm_pending_event { - struct completion *completion; - void (*completion_release)(struct completion *completion); - struct drm_event *event; - struct dma_fence *fence; - struct list_head link; - struct list_head pending_link; - struct drm_file *file_priv; - pid_t pid; /* pid of requester, no guarantee it's valid by the time - we deliver the event, for tracing only */ -}; - -struct drm_prime_file_private { - struct mutex lock; - struct rb_root dmabufs; - struct rb_root handles; -}; - -/** File private data */ -struct drm_file { - unsigned authenticated :1; - /* true when the client has asked us to expose stereo 3D mode flags */ - unsigned stereo_allowed :1; - /* - * true if client understands CRTC primary planes and cursor planes - * in the plane list - */ - unsigned universal_planes:1; - /* true if client understands atomic properties */ - unsigned atomic:1; - /* - * This client is the creator of @master. - * Protected by struct drm_device::master_mutex. - */ - unsigned is_master:1; - - struct pid *pid; - drm_magic_t magic; - struct list_head lhead; - struct drm_minor *minor; - unsigned long lock_count; - - /** Mapping of mm object handles to object pointers. */ - struct idr object_idr; - /** Lock for synchronization of access to object_idr. */ - spinlock_t table_lock; - - struct file *filp; - void *driver_priv; - - struct drm_master *master; /* master this node is currently associated with - N.B. not always dev->master */ - /** - * fbs - List of framebuffers associated with this file. - * - * Protected by fbs_lock. Note that the fbs list holds a reference on - * the fb object to prevent it from untimely disappearing. - */ - struct list_head fbs; - struct mutex fbs_lock; - - /** User-created blob properties; this retains a reference on the - * property. */ - struct list_head blobs; - - wait_queue_head_t event_wait; - struct list_head pending_event_list; - struct list_head event_list; - int event_space; - - struct mutex event_read_lock; - - struct drm_prime_file_private prime; -}; - -/** - * Lock data. - */ -struct drm_lock_data { - struct drm_hw_lock *hw_lock; /**< Hardware lock */ - /** Private of lock holder's file (NULL=kernel) */ - struct drm_file *file_priv; - wait_queue_head_t lock_queue; /**< Queue of blocked processes */ - unsigned long lock_time; /**< Time of last lock in jiffies */ - spinlock_t spinlock; - uint32_t kernel_waiters; - uint32_t user_waiters; - int idle_has_lock; -}; - /* Flags and return codes for get_vblank_timestamp() driver function. */ #define DRM_CALLED_FROM_VBLIRQ 1 #define DRM_VBLANKTIME_SCANOUTPOS_METHOD (1 << 0) @@ -458,13 +370,6 @@ struct drm_lock_data { #define DRM_SCANOUTPOS_IN_VBLANK (1 << 1) #define DRM_SCANOUTPOS_ACCURATE (1 << 2) -enum drm_minor_type { - DRM_MINOR_PRIMARY, - DRM_MINOR_CONTROL, - DRM_MINOR_RENDER, - DRM_MINOR_CNT, -}; - /** * Info file list entry. This structure represents a debugfs or proc file to * be created by the drm core @@ -487,21 +392,6 @@ struct drm_info_node { }; /** - * DRM minor structure. This structure represents a drm minor number. - */ -struct drm_minor { - int index; /**< Minor device number */ - int type; /**< Control or render */ - struct device *kdev; /**< Linux device */ - struct drm_device *dev; - - struct dentry *debugfs_root; - - struct list_head debugfs_list; - struct mutex debugfs_lock; /* Protects debugfs_list. */ -}; - -/** * DRM device structure. This structure represent a complete card that * may contain multiple heads. */ @@ -611,7 +501,6 @@ struct drm_device { struct pci_controller *hose; #endif - struct platform_device *platformdev; /**< Platform device struture */ struct virtio_device *virtdev; struct drm_sg_mem *sg; /**< Scatter gather memory */ @@ -675,21 +564,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev) return ret; } -static inline bool drm_is_render_client(const struct drm_file *file_priv) -{ - return file_priv->minor->type == DRM_MINOR_RENDER; -} - -static inline bool drm_is_control_client(const struct drm_file *file_priv) -{ - return file_priv->minor->type == DRM_MINOR_CONTROL; -} - -static inline bool drm_is_primary_client(const struct drm_file *file_priv) -{ - return file_priv->minor->type == DRM_MINOR_PRIMARY; -} - /******************************************************************/ /** \name Internal function definitions */ /*@{*/ @@ -707,25 +581,6 @@ extern long drm_compat_ioctl(struct file *filp, #endif extern bool drm_ioctl_flags(unsigned int nr, unsigned int *flags); -/* File Operations (drm_fops.c) */ -int drm_open(struct inode *inode, struct file *filp); -ssize_t drm_read(struct file *filp, char __user *buffer, - size_t count, loff_t *offset); -int drm_release(struct inode *inode, struct file *filp); -unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait); -int drm_event_reserve_init_locked(struct drm_device *dev, - struct drm_file *file_priv, - struct drm_pending_event *p, - struct drm_event *e); -int drm_event_reserve_init(struct drm_device *dev, - struct drm_file *file_priv, - struct drm_pending_event *p, - struct drm_event *e); -void drm_event_cancel_free(struct drm_device *dev, - struct drm_pending_event *p); -void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e); -void drm_send_event(struct drm_device *dev, struct drm_pending_event *e); - /* Misc. IOCTL support (drm_ioctl.c) */ int drm_noop(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -759,70 +614,12 @@ static inline int drm_debugfs_remove_files(const struct drm_info_list *files, } #endif -struct dma_buf_export_info; - -extern struct dma_buf *drm_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, - int flags); -extern 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); -extern struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf); -extern 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 *drm_gem_dmabuf_export(struct drm_device *dev, - struct dma_buf_export_info *exp_info); -extern void drm_gem_dmabuf_release(struct dma_buf *dma_buf); - -extern int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages, - dma_addr_t *addrs, int max_pages); -extern struct sg_table *drm_prime_pages_to_sg(struct page **pages, unsigned int nr_pages); -extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg); - - -extern struct drm_dma_handle *drm_pci_alloc(struct drm_device *dev, size_t size, - size_t align); -extern void drm_pci_free(struct drm_device *dev, struct drm_dma_handle * dmah); - /* sysfs support (drm_sysfs.c) */ extern void drm_sysfs_hotplug_event(struct drm_device *dev); /*@}*/ -extern int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver); -extern void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver); -#ifdef CONFIG_PCI -extern int drm_get_pci_dev(struct pci_dev *pdev, - const struct pci_device_id *ent, - struct drm_driver *driver); -extern int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master); -#else -static inline int drm_get_pci_dev(struct pci_dev *pdev, - const struct pci_device_id *ent, - struct drm_driver *driver) -{ - return -ENOSYS; -} - -static inline int drm_pci_set_busid(struct drm_device *dev, - struct drm_master *master) -{ - return -ENOSYS; -} -#endif - -#define DRM_PCIE_SPEED_25 1 -#define DRM_PCIE_SPEED_50 2 -#define DRM_PCIE_SPEED_80 4 - -extern int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *speed_mask); -extern int drm_pcie_get_max_link_width(struct drm_device *dev, u32 *mlw); - -/* platform section */ -extern int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device); - /* returns true if currently okay to sleep */ static __inline__ bool drm_can_sleep(void) { diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index c6f355a970d2..0147a047878d 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -277,6 +277,9 @@ int drm_atomic_connector_set_property(struct drm_connector *connector, * * This function returns the crtc state for the given crtc, or NULL * if the crtc is not part of the global atomic state. + * + * This function is deprecated, @drm_atomic_get_old_crtc_state or + * @drm_atomic_get_new_crtc_state should be used instead. */ static inline struct drm_crtc_state * drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state, @@ -286,12 +289,44 @@ drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state, } /** + * drm_atomic_get_old_crtc_state - get old crtc state, if it exists + * @state: global atomic state object + * @crtc: crtc to grab + * + * This function returns the old crtc state for the given crtc, or + * NULL if the crtc is not part of the global atomic state. + */ +static inline struct drm_crtc_state * +drm_atomic_get_old_crtc_state(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + return state->crtcs[drm_crtc_index(crtc)].old_state; +} +/** + * drm_atomic_get_new_crtc_state - get new crtc state, if it exists + * @state: global atomic state object + * @crtc: crtc to grab + * + * This function returns the new crtc state for the given crtc, or + * NULL if the crtc is not part of the global atomic state. + */ +static inline struct drm_crtc_state * +drm_atomic_get_new_crtc_state(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + return state->crtcs[drm_crtc_index(crtc)].new_state; +} + +/** * drm_atomic_get_existing_plane_state - get plane state, if it exists * @state: global atomic state object * @plane: plane to grab * * This function returns the plane state for the given plane, or NULL * if the plane is not part of the global atomic state. + * + * This function is deprecated, @drm_atomic_get_old_plane_state or + * @drm_atomic_get_new_plane_state should be used instead. */ static inline struct drm_plane_state * drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, @@ -301,12 +336,45 @@ drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, } /** + * drm_atomic_get_old_plane_state - get plane state, if it exists + * @state: global atomic state object + * @plane: plane to grab + * + * This function returns the old plane state for the given plane, or + * NULL if the plane is not part of the global atomic state. + */ +static inline struct drm_plane_state * +drm_atomic_get_old_plane_state(struct drm_atomic_state *state, + struct drm_plane *plane) +{ + return state->planes[drm_plane_index(plane)].old_state; +} + +/** + * drm_atomic_get_new_plane_state - get plane state, if it exists + * @state: global atomic state object + * @plane: plane to grab + * + * This function returns the new plane state for the given plane, or + * NULL if the plane is not part of the global atomic state. + */ +static inline struct drm_plane_state * +drm_atomic_get_new_plane_state(struct drm_atomic_state *state, + struct drm_plane *plane) +{ + return state->planes[drm_plane_index(plane)].new_state; +} + +/** * drm_atomic_get_existing_connector_state - get connector state, if it exists * @state: global atomic state object * @connector: connector to grab * * This function returns the connector state for the given connector, * or NULL if the connector is not part of the global atomic state. + * + * This function is deprecated, @drm_atomic_get_old_connector_state or + * @drm_atomic_get_new_connector_state should be used instead. */ static inline struct drm_connector_state * drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, @@ -321,6 +389,46 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, } /** + * drm_atomic_get_old_connector_state - get connector state, if it exists + * @state: global atomic state object + * @connector: connector to grab + * + * This function returns the old connector state for the given connector, + * or NULL if the connector is not part of the global atomic state. + */ +static inline struct drm_connector_state * +drm_atomic_get_old_connector_state(struct drm_atomic_state *state, + struct drm_connector *connector) +{ + int index = drm_connector_index(connector); + + if (index >= state->num_connector) + return NULL; + + return state->connectors[index].old_state; +} + +/** + * drm_atomic_get_new_connector_state - get connector state, if it exists + * @state: global atomic state object + * @connector: connector to grab + * + * This function returns the new connector state for the given connector, + * or NULL if the connector is not part of the global atomic state. + */ +static inline struct drm_connector_state * +drm_atomic_get_new_connector_state(struct drm_atomic_state *state, + struct drm_connector *connector) +{ + int index = drm_connector_index(connector); + + if (index >= state->num_connector) + return NULL; + + return state->connectors[index].new_state; +} + +/** * __drm_atomic_get_current_plane_state - get current plane state * @state: global atomic state object * @plane: plane to grab diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 9ceda379ce58..dc16274987c7 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -220,10 +220,10 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, __drm_atomic_get_current_plane_state((crtc_state)->state, \ plane))) -/* +/** * drm_atomic_plane_disabling - check whether a plane is being disabled - * @plane: plane object - * @old_state: previous atomic state + * @old_plane_state: old atomic plane state + * @new_plane_state: new atomic plane state * * Checks the atomic state of a plane to determine whether it's being disabled * or not. This also WARNs if it detects an invalid state (both CRTC and FB @@ -233,28 +233,18 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, * True if the plane is being disabled, false otherwise. */ static inline bool -drm_atomic_plane_disabling(struct drm_plane *plane, - struct drm_plane_state *old_state) +drm_atomic_plane_disabling(struct drm_plane_state *old_plane_state, + struct drm_plane_state *new_plane_state) { /* * When disabling a plane, CRTC and FB should always be NULL together. * Anything else should be considered a bug in the atomic core, so we * gently warn about it. */ - WARN_ON((plane->state->crtc == NULL && plane->state->fb != NULL) || - (plane->state->crtc != NULL && plane->state->fb == NULL)); + WARN_ON((new_plane_state->crtc == NULL && new_plane_state->fb != NULL) || + (new_plane_state->crtc != NULL && new_plane_state->fb == NULL)); - /* - * When using the transitional helpers, old_state may be NULL. If so, - * we know nothing about the current state and have to assume that it - * might be enabled. - * - * When using the atomic helpers, old_state won't be NULL. Therefore - * this check assumes that either the driver will have reconstructed - * the correct state in ->reset() or that the driver will have taken - * appropriate measures to disable all planes. - */ - return (!old_state || old_state->crtc) && !plane->state->crtc; + return old_plane_state->crtc && !new_plane_state->crtc; } #endif /* DRM_ATOMIC_HELPER_H_ */ diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h index 1eb4a52cad8d..81a40c2a9a3e 100644 --- a/include/drm/drm_auth.h +++ b/include/drm/drm_auth.h @@ -28,6 +28,23 @@ #ifndef _DRM_AUTH_H_ #define _DRM_AUTH_H_ +/* + * Legacy DRI1 locking data structure. Only here instead of in drm_legacy.h for + * include ordering reasons. + * + * DO NOT USE. + */ +struct drm_lock_data { + struct drm_hw_lock *hw_lock; + struct drm_file *file_priv; + wait_queue_head_t lock_queue; + unsigned long lock_time; + spinlock_t spinlock; + uint32_t kernel_waiters; + uint32_t user_waiters; + int idle_has_lock; +}; + /** * struct drm_master - drm master structure * diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index bda9347554a1..6ef59da3fd8e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -204,6 +204,12 @@ struct drm_crtc_state { * drm_crtc_arm_vblank_event(). See the documentation of that function * for a detailed discussion of the constraints it needs to be used * safely. + * + * If the device can't notify of flip completion in a race-free way + * at all, then the event should be armed just after the page flip is + * committed. In the worst case the driver will send the event to + * userspace one frame too late. This doesn't allow for a real atomic + * update, but it should avoid tearing. */ struct drm_pending_vblank_event *event; @@ -776,6 +782,7 @@ struct drm_crtc { * Debugfs directory for this CRTC. */ struct dentry *debugfs_entry; +#endif /** * @crc: @@ -783,7 +790,6 @@ struct drm_crtc { * Configuration settings of CRC capture. */ struct drm_crtc_crc crc; -#endif /** * @fence_context: diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index ba89295c8651..c0bd0d7651a9 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -789,7 +789,10 @@ struct drm_dp_aux_msg { * @name: user-visible name of this AUX channel and the I2C-over-AUX adapter * @ddc: I2C adapter that can be used for I2C-over-AUX communication * @dev: pointer to struct device that is the parent for this AUX channel + * @crtc: backpointer to the crtc that is currently using this AUX channel * @hw_mutex: internal mutex used for locking transfers + * @crc_work: worker that captures CRCs for each frame + * @crc_count: counter of captured frame CRCs * @transfer: transfers a message representing a single AUX transaction * * The .dev field should be set to a pointer to the device that implements @@ -825,7 +828,10 @@ struct drm_dp_aux { const char *name; struct i2c_adapter ddc; struct device *dev; + struct drm_crtc *crtc; struct mutex hw_mutex; + struct work_struct crc_work; + u8 crc_count; ssize_t (*transfer)(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg); /** @@ -904,4 +910,7 @@ void drm_dp_aux_init(struct drm_dp_aux *aux); int drm_dp_aux_register(struct drm_dp_aux *aux); void drm_dp_aux_unregister(struct drm_dp_aux *aux); +int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc); +int drm_dp_stop_crc(struct drm_dp_aux *aux); + #endif /* _DRM_DP_HELPER_H_ */ diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 4e66fbb56773..4321d012c4ba 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -302,7 +302,6 @@ struct drm_driver { void (*master_drop)(struct drm_device *dev, struct drm_file *file_priv); int (*debugfs_init)(struct drm_minor *minor); - void (*debugfs_cleanup)(struct drm_minor *minor); /** * @gem_free_object: deconstructor for drm_gem_objects diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h new file mode 100644 index 000000000000..d1a25cc17fd1 --- /dev/null +++ b/include/drm/drm_file.h @@ -0,0 +1,172 @@ +/* + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009-2010, Code Aurora Forum. + * All rights reserved. + * + * Author: Rickard E. (Rik) Faith <faith@valinux.com> + * Author: Gareth Hughes <gareth@valinux.com> + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _DRM_FILE_H_ +#define _DRM_FILE_H_ + +#include <linux/types.h> +#include <linux/completion.h> + +#include <uapi/drm/drm.h> + +#include <drm/drm_prime.h> + +struct dma_fence; +struct drm_file; +struct drm_device; + +/* + * FIXME: Not sure we want to have drm_minor here in the end, but to avoid + * header include loops we need it here for now. + */ +enum drm_minor_type { + DRM_MINOR_PRIMARY, + DRM_MINOR_CONTROL, + DRM_MINOR_RENDER, +}; + +/** + * DRM minor structure. This structure represents a drm minor number. + */ +struct drm_minor { + int index; /**< Minor device number */ + int type; /**< Control or render */ + struct device *kdev; /**< Linux device */ + struct drm_device *dev; + + struct dentry *debugfs_root; + + struct list_head debugfs_list; + struct mutex debugfs_lock; /* Protects debugfs_list. */ +}; + +/* Event queued up for userspace to read */ +struct drm_pending_event { + struct completion *completion; + void (*completion_release)(struct completion *completion); + struct drm_event *event; + struct dma_fence *fence; + struct list_head link; + struct list_head pending_link; + struct drm_file *file_priv; + pid_t pid; /* pid of requester, no guarantee it's valid by the time + we deliver the event, for tracing only */ +}; + +/** File private data */ +struct drm_file { + unsigned authenticated :1; + /* true when the client has asked us to expose stereo 3D mode flags */ + unsigned stereo_allowed :1; + /* + * true if client understands CRTC primary planes and cursor planes + * in the plane list + */ + unsigned universal_planes:1; + /* true if client understands atomic properties */ + unsigned atomic:1; + /* + * This client is the creator of @master. + * Protected by struct drm_device::master_mutex. + */ + unsigned is_master:1; + + struct pid *pid; + drm_magic_t magic; + struct list_head lhead; + struct drm_minor *minor; + unsigned long lock_count; + + /** Mapping of mm object handles to object pointers. */ + struct idr object_idr; + /** Lock for synchronization of access to object_idr. */ + spinlock_t table_lock; + + struct file *filp; + void *driver_priv; + + struct drm_master *master; /* master this node is currently associated with + N.B. not always dev->master */ + /** + * fbs - List of framebuffers associated with this file. + * + * Protected by fbs_lock. Note that the fbs list holds a reference on + * the fb object to prevent it from untimely disappearing. + */ + struct list_head fbs; + struct mutex fbs_lock; + + /** User-created blob properties; this retains a reference on the + * property. */ + struct list_head blobs; + + wait_queue_head_t event_wait; + struct list_head pending_event_list; + struct list_head event_list; + int event_space; + + struct mutex event_read_lock; + + struct drm_prime_file_private prime; +}; + +static inline bool drm_is_render_client(const struct drm_file *file_priv) +{ + return file_priv->minor->type == DRM_MINOR_RENDER; +} + +static inline bool drm_is_control_client(const struct drm_file *file_priv) +{ + return file_priv->minor->type == DRM_MINOR_CONTROL; +} + +static inline bool drm_is_primary_client(const struct drm_file *file_priv) +{ + return file_priv->minor->type == DRM_MINOR_PRIMARY; +} + +int drm_open(struct inode *inode, struct file *filp); +ssize_t drm_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset); +int drm_release(struct inode *inode, struct file *filp); +unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait); +int drm_event_reserve_init_locked(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_pending_event *p, + struct drm_event *e); +int drm_event_reserve_init(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_pending_event *p, + struct drm_event *e); +void drm_event_cancel_free(struct drm_device *dev, + struct drm_pending_event *p); +void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e); +void drm_send_event(struct drm_device *dev, struct drm_pending_event *e); + +#endif /* _DRM_FILE_H_ */ diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index 3b2a28f7f49f..b9ade75ecd82 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -34,6 +34,10 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include <linux/kref.h> + +#include <drm/drm_vma_manager.h> + /** * struct drm_gem_object - GEM buffer object * diff --git a/include/drm/drm_pci.h b/include/drm/drm_pci.h new file mode 100644 index 000000000000..f5ebfcaf69e0 --- /dev/null +++ b/include/drm/drm_pci.h @@ -0,0 +1,75 @@ +/* + * Internal Header for the Direct Rendering Manager + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009-2010, Code Aurora Forum. + * All rights reserved. + * + * Author: Rickard E. (Rik) Faith <faith@valinux.com> + * Author: Gareth Hughes <gareth@valinux.com> + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _DRM_PCI_H_ +#define _DRM_PCI_H_ + +#include <linux/pci.h> + +struct drm_dma_handle; +struct drm_device; +struct drm_driver; +struct drm_master; + +extern struct drm_dma_handle *drm_pci_alloc(struct drm_device *dev, size_t size, + size_t align); +extern void drm_pci_free(struct drm_device *dev, struct drm_dma_handle * dmah); + +extern int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver); +extern void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver); +#ifdef CONFIG_PCI +extern int drm_get_pci_dev(struct pci_dev *pdev, + const struct pci_device_id *ent, + struct drm_driver *driver); +extern int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master); +#else +static inline int drm_get_pci_dev(struct pci_dev *pdev, + const struct pci_device_id *ent, + struct drm_driver *driver) +{ + return -ENOSYS; +} + +static inline int drm_pci_set_busid(struct drm_device *dev, + struct drm_master *master) +{ + return -ENOSYS; +} +#endif + +#define DRM_PCIE_SPEED_25 1 +#define DRM_PCIE_SPEED_50 2 +#define DRM_PCIE_SPEED_80 4 + +extern int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *speed_mask); +extern int drm_pcie_get_max_link_width(struct drm_device *dev, u32 *mlw); + +#endif /* _DRM_PCI_H_ */ diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h new file mode 100644 index 000000000000..d09563ecc4b7 --- /dev/null +++ b/include/drm/drm_prime.h @@ -0,0 +1,80 @@ +/* + * Copyright © 2012 Red Hat + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009-2010, Code Aurora Forum. + * + * 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 (including the next + * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * Rob Clark <rob.clark@linaro.org> + * + */ + +#ifndef __DRM_PRIME_H__ +#define __DRM_PRIME_H__ + +#include <linux/mutex.h> +#include <linux/rbtree.h> +#include <linux/scatterlist.h> + +/** + * struct drm_prime_file_private - per-file tracking for PRIME + * + * This just contains the internal &struct dma_buf and handle caches for each + * &struct drm_file used by the PRIME core code. + */ + +struct drm_prime_file_private { +/* private: */ + struct mutex lock; + struct rb_root dmabufs; + struct rb_root handles; +}; + +struct dma_buf_export_info; +struct dma_buf; + +struct drm_device; +struct drm_gem_object; +struct drm_file; + +extern struct dma_buf *drm_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, + int flags); +extern 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); +extern struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); +extern 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 *drm_gem_dmabuf_export(struct drm_device *dev, + struct dma_buf_export_info *exp_info); +extern void drm_gem_dmabuf_release(struct dma_buf *dma_buf); + +extern int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages, + dma_addr_t *addrs, int max_pages); +extern struct sg_table *drm_prime_pages_to_sg(struct page **pages, unsigned int nr_pages); +extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg); + + +#endif /* __DRM_PRIME_H__ */ diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h index 9c03895dc479..d84d52f6d2b1 100644 --- a/include/drm/drm_vma_manager.h +++ b/include/drm/drm_vma_manager.h @@ -25,7 +25,6 @@ #include <drm/drm_mm.h> #include <linux/mm.h> -#include <linux/module.h> #include <linux/rbtree.h> #include <linux/spinlock.h> #include <linux/types.h> diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index ef20abb8119b..995c8f9c692f 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -114,6 +114,20 @@ extern "C" { #define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ /* + * 2 plane RGB + A + * index 0 = RGB plane, same format as the corresponding non _A8 format has + * index 1 = A plane, [7:0] A + */ +#define DRM_FORMAT_XRGB8888_A8 fourcc_code('X', 'R', 'A', '8') +#define DRM_FORMAT_XBGR8888_A8 fourcc_code('X', 'B', 'A', '8') +#define DRM_FORMAT_RGBX8888_A8 fourcc_code('R', 'X', 'A', '8') +#define DRM_FORMAT_BGRX8888_A8 fourcc_code('B', 'X', 'A', '8') +#define DRM_FORMAT_RGB888_A8 fourcc_code('R', '8', 'A', '8') +#define DRM_FORMAT_BGR888_A8 fourcc_code('B', '8', 'A', '8') +#define DRM_FORMAT_RGB565_A8 fourcc_code('R', '5', 'A', '8') +#define DRM_FORMAT_BGR565_A8 fourcc_code('B', '5', 'A', '8') + +/* * 2 plane YCbCr * index 0 = Y plane, [7:0] Y * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index 53cd07ccaa4c..8cb07680fb41 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -161,6 +161,28 @@ enum ipu_channel_irq { #define IPUV3_CHANNEL_MEM_BG_ASYNC_ALPHA 52 #define IPUV3_NUM_CHANNELS 64 +static inline int ipu_channel_alpha_channel(int ch_num) +{ + switch (ch_num) { + case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: + return IPUV3_CHANNEL_G_MEM_IC_PRP_VF_ALPHA; + case IPUV3_CHANNEL_G_MEM_IC_PP: + return IPUV3_CHANNEL_G_MEM_IC_PP_ALPHA; + case IPUV3_CHANNEL_MEM_FG_SYNC: + return IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA; + case IPUV3_CHANNEL_MEM_FG_ASYNC: + return IPUV3_CHANNEL_MEM_FG_ASYNC_ALPHA; + case IPUV3_CHANNEL_MEM_BG_SYNC: + return IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA; + case IPUV3_CHANNEL_MEM_BG_ASYNC: + return IPUV3_CHANNEL_MEM_BG_ASYNC_ALPHA; + case IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB: + return IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB_ALPHA; + default: + return -EINVAL; + } +} + int ipu_map_irq(struct ipu_soc *ipu, int irq); int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, enum ipu_channel_irq irq); @@ -300,7 +322,7 @@ struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow); void ipu_dp_put(struct ipu_dp *); int ipu_dp_enable(struct ipu_soc *ipu); int ipu_dp_enable_channel(struct ipu_dp *dp); -void ipu_dp_disable_channel(struct ipu_dp *dp); +void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync); void ipu_dp_disable(struct ipu_soc *ipu); int ipu_dp_setup_channel(struct ipu_dp *dp, enum ipu_color_space in, enum ipu_color_space out); @@ -309,6 +331,21 @@ int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha, bool bg_chan); /* + * IPU Prefetch Resolve Gasket (prg) functions + */ +int ipu_prg_max_active_channels(void); +bool ipu_prg_present(struct ipu_soc *ipu); +bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, + uint64_t modifier); +int ipu_prg_enable(struct ipu_soc *ipu); +void ipu_prg_disable(struct ipu_soc *ipu); +void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan); +int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, + unsigned int axi_id, unsigned int width, + unsigned int height, unsigned int stride, + u32 format, unsigned long *eba); + +/* * IPU CMOS Sensor Interface (csi) functions */ struct ipu_csi; |