diff --git a/.mailmap b/.mailmap index f4f18b06f2f1..d8a1f19c381b 100644 --- a/.mailmap +++ b/.mailmap @@ -628,7 +628,8 @@ Nadav Amit Nadia Yvette Chambers William Lee Irwin III Naoya Horiguchi Naoya Horiguchi -Natalie Vock +Natalie Vock +Natalie Vock Nathan Chancellor Naveen N Rao Naveen N Rao diff --git a/Documentation/devicetree/bindings/display/panel/novatek,nt36536.yaml b/Documentation/devicetree/bindings/display/panel/novatek,nt36536.yaml new file mode 100644 index 000000000000..1e86f5329b22 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/novatek,nt36536.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/novatek,nt36536.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Novatek NT36536 based DSI display Panels + +maintainers: + - Pengyu Luo + +description: + The Novatek NT36536 is a generic DSI Panel IC used to drive dsi + panels. Support video mode panels from China Star Optoelectronics + Technology (CSOT), such as PP8807HB1-1 which is a dual-link 10-bit + panel. + +allOf: + - $ref: panel-common-dual.yaml# + +properties: + compatible: + items: + - enum: + - csot,pp8807hb1-1 + - const: novatek,nt36536 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + vddio-supply: + description: I/O source voltage rail + + vsp-supply: + description: Positive source voltage rail + + vsn-supply: + description: Negative source voltage rail + + backlight: true + ports: true + +required: + - compatible + - reg + - vddio-supply + - reset-gpios + - ports + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "csot,pp8807hb1-1", "novatek,nt36536"; + reg = <0>; + + vddio-supply = <&vreg_iovdd_1p8>; + reset-gpios = <&tlmm 98 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + panel_in_0: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1{ + reg = <1>; + panel_in_1: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml index b89f86bc0683..8382d4920bfd 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml @@ -58,6 +58,8 @@ properties: - hydis,hv070wx2-1e0 # Jenson Display BL-JT60050-01A 7" WSVGA (1024x600) color TFT LCD LVDS panel - jenson,bl-jt60050-01a + # Opto Logic SCX1001511GGC49 10.1" WXGA (1280x800) TFT LCD LVDS panel + - optologic,scx1001511ggc49 # Riverdi RVT101HVLNWC00 10.1" WXGA (1280x800) TFT LCD LVDS panel - riverdi,rvt101hvlnwc00 # Riverdi RVT70HSLNWCA0 7.0" WSVGA (1024x600) TFT LCD LVDS panel diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml index 7200095ef19e..bdf981781bd5 100644 --- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml @@ -22,6 +22,7 @@ properties: - nvidia,tegra186-vic - nvidia,tegra194-vic - nvidia,tegra234-vic + - nvidia,tegra264-vic - items: - const: nvidia,tegra132-vic diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml index 3563378a01af..8312b7699cbe 100644 --- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml @@ -25,6 +25,7 @@ properties: - nvidia,tegra186-host1x - nvidia,tegra194-host1x - nvidia,tegra234-host1x + - nvidia,tegra264-host1x - items: - const: nvidia,tegra132-host1x @@ -57,7 +58,8 @@ properties: enum: [1, 2] ranges: - maxItems: 1 + minItems: 1 + maxItems: 2 clocks: description: Must contain one entry, for the module clock. See @@ -192,6 +194,7 @@ allOf: contains: enum: - nvidia,tegra234-host1x + - nvidia,tegra264-host1x then: properties: reg-names: @@ -239,6 +242,21 @@ allOf: required: - reg-names + - if: + properties: + compatible: + contains: + enum: + - nvidia,tegra264-host1x + then: + properties: + ranges: + minItems: 2 + maxItems: 2 + else: + properties: + ranges: + maxItems: 1 examples: - | diff --git a/MAINTAINERS b/MAINTAINERS index 6dea93a41962..721ecafe4b2a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6636,7 +6636,7 @@ F: tools/testing/selftests/cgroup/test_cpuset_v1_base.sh CONTROL GROUP - DEVICE MEMORY CONTROLLER (DMEM) M: Maarten Lankhorst M: Maxime Ripard -M: Natalie Vock +M: Natalie Vock L: cgroups@vger.kernel.org L: dri-devel@lists.freedesktop.org S: Maintained diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index 5a8f7dfb54e6..b78b7c35cd9b 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -318,7 +318,7 @@ static int ivpu_fw_parse(struct ivpu_device *vdev) fw->shave_nn_size = PAGE_ALIGN(fw_hdr->shave_nn_fw_size); fw->cold_boot_entry_point = fw_hdr->entry_point; - fw->trace_level = min_t(u32, ivpu_fw_log_level, IVPU_FW_LOG_FATAL); + fw->trace_level = min(ivpu_fw_log_level, IVPU_FW_LOG_FATAL); fw->trace_destination_mask = VPU_TRACE_DESTINATION_VERBOSE_TRACING; fw->trace_hw_component_mask = -1; diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c index c4014c83e727..bb3ca81838e3 100644 --- a/drivers/accel/ivpu/ivpu_mmu_context.c +++ b/drivers/accel/ivpu/ivpu_mmu_context.c @@ -588,8 +588,8 @@ void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ct start = vdev->hw->ranges.runtime.start; end = vdev->hw->ranges.shave.end; } else { - start = min_t(u64, vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start); - end = max_t(u64, vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end); + start = min(vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start); + end = max(vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end); } drm_mm_init(&ctx->mm, start, end - start); diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c index eba5c6dcb5ad..d152c02419f5 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -663,6 +663,11 @@ static bool anx6345_get_chip_id(struct anx6345 *anx6345) return false; } +static void anx6345_panel_put_action(void *data) +{ + drm_panel_put(data); +} + static int anx6345_i2c_probe(struct i2c_client *client) { struct anx6345 *anx6345; @@ -691,6 +696,13 @@ static int anx6345_i2c_probe(struct i2c_client *client) if (err) DRM_DEBUG("No panel found\n"); + if (anx6345->panel) { + err = devm_add_action_or_reset(dev, anx6345_panel_put_action, + anx6345->panel); + if (err) + return err; + } + /* 1.2V digital core power regulator */ anx6345->dvdd12 = devm_regulator_get(dev, "dvdd12"); if (IS_ERR(anx6345->dvdd12)) { diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c index cc1f88e7873e..26cc72948f31 100644 --- a/drivers/gpu/drm/bridge/fsl-ldb.c +++ b/drivers/gpu/drm/bridge/fsl-ldb.c @@ -348,6 +348,7 @@ static int fsl_ldb_probe(struct platform_device *pdev) fsl_ldb->use_termination_resistor = true; fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(fsl_ldb->panel_bridge)) return PTR_ERR(fsl_ldb->panel_bridge); diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index f39d83a5ae37..6d97ae5640c4 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -660,6 +660,7 @@ static int lt9211_parse_dt(struct lt9211 *ctx) return ret; if (panel) { panel_bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(panel_bridge)) return PTR_ERR(panel_bridge); } diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index a82ea0c944eb..f97c8586273d 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -156,6 +156,7 @@ static int lvds_codec_probe(struct platform_device *pdev) lvds_codec->panel_bridge = devm_drm_panel_bridge_add_typed(dev, panel, lvds_codec->connector_type); + drm_panel_put(panel); if (IS_ERR(lvds_codec->panel_bridge)) return PTR_ERR(lvds_codec->panel_bridge); diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 62af499f1f5c..02388a3de626 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -294,7 +294,7 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, return (void *)panel_bridge; panel_bridge->connector_type = connector_type; - panel_bridge->panel = panel; + panel_bridge->panel = drm_panel_get(panel); panel_bridge->bridge.of_node = panel->dev->of_node; panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; @@ -316,6 +316,7 @@ EXPORT_SYMBOL(drm_panel_bridge_add_typed); void drm_panel_bridge_remove(struct drm_bridge *bridge) { struct panel_bridge *panel_bridge; + struct drm_panel *panel; if (!bridge) return; @@ -326,10 +327,12 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge) } panel_bridge = drm_bridge_to_panel_bridge(bridge); + panel = panel_bridge->panel; drm_bridge_remove(bridge); /* TODO remove this after reworking panel_bridge lifetime */ - devm_drm_put_bridge(panel_bridge->panel->dev, bridge); + devm_drm_put_bridge(panel->dev, bridge); + drm_panel_put(panel); } EXPORT_SYMBOL(drm_panel_bridge_remove); @@ -357,11 +360,16 @@ EXPORT_SYMBOL(drm_panel_bridge_set_orientation); static void devm_drm_panel_bridge_release(struct device *dev, void *res) { struct drm_bridge *bridge = *(struct drm_bridge **)res; + struct panel_bridge *panel_bridge; + struct drm_panel *panel; if (!bridge) return; + panel_bridge = drm_bridge_to_panel_bridge(bridge); + panel = panel_bridge->panel; drm_bridge_remove(bridge); + drm_panel_put(panel); } /** @@ -507,8 +515,10 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, if (ret) return ERR_PTR(ret); - if (panel) + if (panel) { bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); + } return bridge; } @@ -541,8 +551,10 @@ struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, if (ret) return ERR_PTR(ret); - if (panel) + if (panel) { bridge = drmm_panel_bridge_add(drm, panel); + drm_panel_put(panel); + } return bridge; } diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index 25ab475309e8..e2fc69fc51b6 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -1934,6 +1934,7 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, panel = of_drm_find_panel(remote); if (!IS_ERR(panel)) { next_bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(next_bridge)) { ret = PTR_ERR(next_bridge); next_bridge = NULL; // Inhibit the cleanup action on an ERR_PTR diff --git a/drivers/gpu/drm/bridge/ssd2825.c b/drivers/gpu/drm/bridge/ssd2825.c index 00a4ed4a0700..b0142d6d40dc 100644 --- a/drivers/gpu/drm/bridge/ssd2825.c +++ b/drivers/gpu/drm/bridge/ssd2825.c @@ -301,6 +301,7 @@ static int ssd2825_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_d if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DSI); + drm_panel_put(panel); if (IS_ERR(bridge)) return PTR_ERR(bridge); } diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index ce1b73214f26..8f1648f799a7 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -2329,6 +2329,7 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc) if (panel) { bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(bridge)) return PTR_ERR(bridge); } @@ -2359,6 +2360,7 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc) struct drm_bridge *panel_bridge; panel_bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(panel_bridge)) return PTR_ERR(panel_bridge); diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 0d85120fcc7a..112a421f99b5 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -479,6 +479,7 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host, if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DSI); + drm_panel_put(panel); if (IS_ERR(bridge)) return PTR_ERR(bridge); diff --git a/drivers/gpu/drm/bridge/waveshare-dsi.c b/drivers/gpu/drm/bridge/waveshare-dsi.c index 9c9825c9b7c5..420f3b870a74 100644 --- a/drivers/gpu/drm/bridge/waveshare-dsi.c +++ b/drivers/gpu/drm/bridge/waveshare-dsi.c @@ -169,6 +169,7 @@ static int ws_bridge_probe(struct i2c_client *i2c) return dev_err_probe(dev, ret, "Failed to find remote panel\n"); ws->next_bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(ws->next_bridge)) return PTR_ERR(ws->next_bridge); diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 1f4cff6bb4a7..e495117735e1 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -269,7 +269,8 @@ EXPORT_SYMBOL_GPL(drm_of_get_panel_orientation); * @np: device tree node containing encoder output ports * @port: port in the device tree node * @endpoint: endpoint in the device tree node - * @panel: pointer to hold returned drm_panel, must not be NULL + * @panel: pointer to hold returned drm_panel, must not be NULL. On success + * the caller must call drm_panel_put() when done with the panel * @bridge: pointer to hold returned drm_bridge * * Given a DT node's port and endpoint number, find the connected node and diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index d7c6f4824b2d..f8f6082e637f 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -82,6 +82,7 @@ static void drm_panel_init(struct drm_panel *panel, struct device *dev, */ void drm_panel_add(struct drm_panel *panel) { + drm_panel_get(panel); mutex_lock(&panel_lock); list_add_tail(&panel->list, &panel_list); mutex_unlock(&panel_lock); @@ -99,6 +100,7 @@ void drm_panel_remove(struct drm_panel *panel) mutex_lock(&panel_lock); list_del_init(&panel->list); mutex_unlock(&panel_lock); + drm_panel_put(panel); } EXPORT_SYMBOL(drm_panel_remove); @@ -457,14 +459,17 @@ EXPORT_SYMBOL(__devm_drm_panel_alloc); #ifdef CONFIG_OF /** - * of_drm_find_panel - look up a panel using a device tree node + * of_drm_find_panel - look up and reference a panel by device tree node * @np: device tree node of the panel * * Searches the set of registered panels for one that matches the given device - * tree node. If a matching panel is found, return a pointer to it. + * tree node. If a matching panel is found, the panel's reference count is + * incremented before returning a pointer to it. The caller must call + * drm_panel_put() when it no longer needs the panel pointer. * - * Return: A pointer to the panel registered for the specified device tree - * node or an ERR_PTR() if no panel matching the device tree node can be found. + * Return: A reference-counted pointer to the panel registered for the specified + * device tree node or an ERR_PTR() if no panel matching the device tree node + * can be found. * * Possible error codes returned by this function: * @@ -483,6 +488,7 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np) list_for_each_entry(panel, &panel_list, list) { if (panel->dev->of_node == np) { + drm_panel_get(panel); mutex_unlock(&panel_lock); return panel; } @@ -494,7 +500,13 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np) EXPORT_SYMBOL(of_drm_find_panel); #endif -/* Find panel by fwnode. This should be identical to of_drm_find_panel(). */ +/* + * Find panel by fwnode, returning a counted reference. + * + * Behaves identically to of_drm_find_panel(). On success the returned + * pointer has been passed through drm_panel_get(); the caller must call + * drm_panel_put() when done with it. + */ static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode) { struct drm_panel *panel; @@ -506,6 +518,7 @@ static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode list_for_each_entry(panel, &panel_list, list) { if (dev_fwnode(panel->dev) == fwnode) { + drm_panel_get(panel); mutex_unlock(&panel_lock); return panel; } @@ -642,6 +655,7 @@ void drm_panel_remove_follower(struct drm_panel_follower *follower) mutex_unlock(&panel->follower_lock); put_device(panel->dev); + drm_panel_put(panel); } EXPORT_SYMBOL(drm_panel_remove_follower); diff --git a/drivers/gpu/drm/drm_panel.c.orig b/drivers/gpu/drm/drm_panel.c.orig new file mode 100644 index 000000000000..32fba1a562fb --- /dev/null +++ b/drivers/gpu/drm/drm_panel.c.orig @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2013, NVIDIA Corporation. 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, sub license, + * 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 NON-INFRINGEMENT. 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static DEFINE_MUTEX(panel_lock); +static LIST_HEAD(panel_list); + +/** + * DOC: drm panel + * + * The DRM panel helpers allow drivers to register panel objects with a + * central registry and provide functions to retrieve those panels in display + * drivers. + * + * For easy integration into drivers using the &drm_bridge infrastructure please + * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add(). + */ + +/** + * drm_panel_init - initialize a panel + * @panel: DRM panel + * @dev: parent device of the panel + * @funcs: panel operations + * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to + * the panel interface (must NOT be DRM_MODE_CONNECTOR_Unknown) + * + * Initialize the panel structure for subsequent registration with + * drm_panel_add(). + */ +static void drm_panel_init(struct drm_panel *panel, struct device *dev, + const struct drm_panel_funcs *funcs, + int connector_type) +{ + if (connector_type == DRM_MODE_CONNECTOR_Unknown) + DRM_WARN("%s: %s: a valid connector type is required!\n", __func__, dev_name(dev)); + + INIT_LIST_HEAD(&panel->list); + INIT_LIST_HEAD(&panel->followers); + mutex_init(&panel->follower_lock); + panel->dev = dev; + panel->funcs = funcs; + panel->connector_type = connector_type; +} + +/** + * drm_panel_add - add a panel to the global registry + * @panel: panel to add + * + * Add a panel to the global registry so that it can be looked + * up by display drivers. The panel to be added must have been + * allocated by devm_drm_panel_alloc(). + */ +void drm_panel_add(struct drm_panel *panel) +{ + drm_panel_get(panel); + mutex_lock(&panel_lock); + list_add_tail(&panel->list, &panel_list); + mutex_unlock(&panel_lock); +} +EXPORT_SYMBOL(drm_panel_add); + +/** + * drm_panel_remove - remove a panel from the global registry + * @panel: DRM panel + * + * Removes a panel from the global registry. + */ +void drm_panel_remove(struct drm_panel *panel) +{ + mutex_lock(&panel_lock); + list_del_init(&panel->list); + mutex_unlock(&panel_lock); + drm_panel_put(panel); +} +EXPORT_SYMBOL(drm_panel_remove); + +static void drm_panel_add_release(void *data) +{ + drm_panel_remove(data); +} + +/** + * devm_drm_panel_add - add a panel to the global registry using devres + * @dev: device to which the panel is attached + * @panel: panel to add + * + * Add a panel to the global registry so that it can be looked + * up by display drivers. The panel to be added must have been + * allocated by devm_drm_panel_alloc(). Unlike drm_panel_add() with this + * function there is no need to call drm_panel_remove(), it will be called + * automatically. + */ +int devm_drm_panel_add(struct device *dev, struct drm_panel *panel) +{ + drm_panel_add(panel); + + return devm_add_action_or_reset(dev, drm_panel_add_release, panel); +} +EXPORT_SYMBOL(devm_drm_panel_add); + +/** + * drm_panel_prepare - power on a panel + * @panel: DRM panel + * + * Calling this function will enable power and deassert any reset signals to + * the panel. After this has completed it is possible to communicate with any + * integrated circuitry via a command bus. This function cannot fail (as it is + * called from the pre_enable call chain). There will always be a call to + * drm_panel_disable() afterwards. + */ +void drm_panel_prepare(struct drm_panel *panel) +{ + struct drm_panel_follower *follower; + int ret; + + if (!panel) + return; + + if (panel->prepared) { + dev_warn(panel->dev, "Skipping prepare of already prepared panel\n"); + return; + } + + mutex_lock(&panel->follower_lock); + + if (panel->funcs && panel->funcs->prepare) { + ret = panel->funcs->prepare(panel); + if (ret < 0) + goto exit; + } + panel->prepared = true; + + list_for_each_entry(follower, &panel->followers, list) { + if (!follower->funcs->panel_prepared) + continue; + + ret = follower->funcs->panel_prepared(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_prepared, ret); + } + +exit: + mutex_unlock(&panel->follower_lock); +} +EXPORT_SYMBOL(drm_panel_prepare); + +/** + * drm_panel_unprepare - power off a panel + * @panel: DRM panel + * + * Calling this function will completely power off a panel (assert the panel's + * reset, turn off power supplies, ...). After this function has completed, it + * is usually no longer possible to communicate with the panel until another + * call to drm_panel_prepare(). + */ +void drm_panel_unprepare(struct drm_panel *panel) +{ + struct drm_panel_follower *follower; + int ret; + + if (!panel) + return; + + /* + * If you are seeing the warning below it likely means one of two things: + * - Your panel driver incorrectly calls drm_panel_unprepare() in its + * shutdown routine. You should delete this. + * - You are using panel-edp or panel-simple and your DRM modeset + * driver's shutdown() callback happened after the panel's shutdown(). + * In this case the warning is harmless though ideally you should + * figure out how to reverse the order of the shutdown() callbacks. + */ + if (!panel->prepared) { + dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n"); + return; + } + + mutex_lock(&panel->follower_lock); + + list_for_each_entry(follower, &panel->followers, list) { + if (!follower->funcs->panel_unpreparing) + continue; + + ret = follower->funcs->panel_unpreparing(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_unpreparing, ret); + } + + if (panel->funcs && panel->funcs->unprepare) { + ret = panel->funcs->unprepare(panel); + if (ret < 0) + goto exit; + } + panel->prepared = false; + +exit: + mutex_unlock(&panel->follower_lock); +} +EXPORT_SYMBOL(drm_panel_unprepare); + +/** + * drm_panel_enable - enable a panel + * @panel: DRM panel + * + * Calling this function will cause the panel display drivers to be turned on + * and the backlight to be enabled. Content will be visible on screen after + * this call completes. This function cannot fail (as it is called from the + * enable call chain). There will always be a call to drm_panel_disable() + * afterwards. + */ +void drm_panel_enable(struct drm_panel *panel) +{ + struct drm_panel_follower *follower; + int ret; + + if (!panel) + return; + + if (panel->enabled) { + dev_warn(panel->dev, "Skipping enable of already enabled panel\n"); + return; + } + + mutex_lock(&panel->follower_lock); + + if (panel->funcs && panel->funcs->enable) { + ret = panel->funcs->enable(panel); + if (ret < 0) + goto exit; + } + panel->enabled = true; + + ret = backlight_enable(panel->backlight); + if (ret < 0) + DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n", + ret); + + list_for_each_entry(follower, &panel->followers, list) { + if (!follower->funcs->panel_enabled) + continue; + + ret = follower->funcs->panel_enabled(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_enabled, ret); + } + +exit: + mutex_unlock(&panel->follower_lock); +} +EXPORT_SYMBOL(drm_panel_enable); + +/** + * drm_panel_disable - disable a panel + * @panel: DRM panel + * + * This will typically turn off the panel's backlight or disable the display + * drivers. For smart panels it should still be possible to communicate with + * the integrated circuitry via any command bus after this call. + */ +void drm_panel_disable(struct drm_panel *panel) +{ + struct drm_panel_follower *follower; + int ret; + + if (!panel) + return; + + /* + * If you are seeing the warning below it likely means one of two things: + * - Your panel driver incorrectly calls drm_panel_disable() in its + * shutdown routine. You should delete this. + * - You are using panel-edp or panel-simple and your DRM modeset + * driver's shutdown() callback happened after the panel's shutdown(). + * In this case the warning is harmless though ideally you should + * figure out how to reverse the order of the shutdown() callbacks. + */ + if (!panel->enabled) { + dev_warn(panel->dev, "Skipping disable of already disabled panel\n"); + return; + } + + mutex_lock(&panel->follower_lock); + + list_for_each_entry(follower, &panel->followers, list) { + if (!follower->funcs->panel_disabling) + continue; + + ret = follower->funcs->panel_disabling(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_disabling, ret); + } + + ret = backlight_disable(panel->backlight); + if (ret < 0) + DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n", + ret); + + if (panel->funcs && panel->funcs->disable) { + ret = panel->funcs->disable(panel); + if (ret < 0) + goto exit; + } + panel->enabled = false; + +exit: + mutex_unlock(&panel->follower_lock); +} +EXPORT_SYMBOL(drm_panel_disable); + +/** + * drm_panel_get_modes - probe the available display modes of a panel + * @panel: DRM panel + * @connector: DRM connector + * + * The modes probed from the panel are automatically added to the connector + * that the panel is attached to. + * + * Return: The number of modes available from the panel on success, or 0 on + * failure (no modes). + */ +int drm_panel_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + if (!panel) + return 0; + + if (panel->funcs && panel->funcs->get_modes) { + int num; + + num = panel->funcs->get_modes(panel, connector); + if (num > 0) + return num; + } + + return 0; +} +EXPORT_SYMBOL(drm_panel_get_modes); + +static void __drm_panel_free(struct kref *kref) +{ + struct drm_panel *panel = container_of(kref, struct drm_panel, refcount); + + kfree(panel->container); +} + +/** + * drm_panel_get - Acquire a panel reference + * @panel: DRM panel + * + * This function increments the panel's refcount. + * Returns: + * Pointer to @panel + */ +struct drm_panel *drm_panel_get(struct drm_panel *panel) +{ + if (!panel) + return panel; + + kref_get(&panel->refcount); + + return panel; +} +EXPORT_SYMBOL(drm_panel_get); + +/** + * drm_panel_put - Release a panel reference + * @panel: DRM panel + * + * This function decrements the panel's reference count and frees the + * object if the reference count drops to zero. + */ +void drm_panel_put(struct drm_panel *panel) +{ + if (panel) + kref_put(&panel->refcount, __drm_panel_free); +} +EXPORT_SYMBOL(drm_panel_put); + +/** + * drm_panel_put_void - wrapper to drm_panel_put() taking a void pointer + * + * @data: pointer to @struct drm_panel, cast to a void pointer + * + * Wrapper of drm_panel_put() to be used when a function taking a void + * pointer is needed, for example as a devm action. + */ +static void drm_panel_put_void(void *data) +{ + struct drm_panel *panel = (struct drm_panel *)data; + + drm_panel_put(panel); +} + +void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset, + const struct drm_panel_funcs *funcs, + int connector_type) +{ + void *container; + struct drm_panel *panel; + int err; + + if (!funcs) { + dev_warn(dev, "Missing funcs pointer\n"); + return ERR_PTR(-EINVAL); + } + + container = kzalloc(size, GFP_KERNEL); + if (!container) + return ERR_PTR(-ENOMEM); + + panel = container + offset; + panel->container = container; + panel->funcs = funcs; + kref_init(&panel->refcount); + + err = devm_add_action_or_reset(dev, drm_panel_put_void, panel); + if (err) + return ERR_PTR(err); + + drm_panel_init(panel, dev, funcs, connector_type); + + return container; +} +EXPORT_SYMBOL(__devm_drm_panel_alloc); + +#ifdef CONFIG_OF +/** + * of_drm_find_panel - look up and reference a panel by device tree node + * @np: device tree node of the panel + * + * Searches the set of registered panels for one that matches the given device + * tree node. If a matching panel is found, the panel's reference count is + * incremented before returning a pointer to it. The caller must call + * drm_panel_put() when it no longer needs the panel pointer. + * + * Return: A reference-counted pointer to the panel registered for the specified + * device tree node or an ERR_PTR() if no panel matching the device tree node + * can be found. + * + * Possible error codes returned by this function: + * + * - EPROBE_DEFER: the panel device has not been probed yet, and the caller + * should retry later + * - ENODEV: the device is not available (status != "okay" or "ok") + */ +struct drm_panel *of_drm_find_panel(const struct device_node *np) +{ + struct drm_panel *panel; + + if (!of_device_is_available(np)) + return ERR_PTR(-ENODEV); + + mutex_lock(&panel_lock); + + list_for_each_entry(panel, &panel_list, list) { + if (panel->dev->of_node == np) { + drm_panel_get(panel); + mutex_unlock(&panel_lock); + return panel; + } + } + + mutex_unlock(&panel_lock); + return ERR_PTR(-EPROBE_DEFER); +} +EXPORT_SYMBOL(of_drm_find_panel); +#endif + +/* Find panel by fwnode. This should be identical to of_drm_find_panel(). */ +static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode) +{ + struct drm_panel *panel; + + if (!fwnode_device_is_available(fwnode)) + return ERR_PTR(-ENODEV); + + mutex_lock(&panel_lock); + + list_for_each_entry(panel, &panel_list, list) { + if (dev_fwnode(panel->dev) == fwnode) { + mutex_unlock(&panel_lock); + return panel; + } + } + + mutex_unlock(&panel_lock); + + return ERR_PTR(-EPROBE_DEFER); +} + +/* Find panel by follower device */ +static struct drm_panel *find_panel_by_dev(struct device *follower_dev) +{ + struct fwnode_handle *fwnode; + struct drm_panel *panel; + + fwnode = fwnode_find_reference(dev_fwnode(follower_dev), "panel", 0); + if (IS_ERR(fwnode)) + return ERR_PTR(-ENODEV); + + panel = find_panel_by_fwnode(fwnode); + fwnode_handle_put(fwnode); + + return panel; +} + +/** + * drm_is_panel_follower() - Check if the device is a panel follower + * @dev: The 'struct device' to check + * + * This checks to see if a device needs to be power sequenced together with + * a panel using the panel follower API. + * + * The "panel" property of the follower points to the panel to be followed. + * + * Return: true if we should be power sequenced with a panel; false otherwise. + */ +bool drm_is_panel_follower(struct device *dev) +{ + /* + * The "panel" property is actually a phandle, but for simplicity we + * don't bother trying to parse it here. We just need to know if the + * property is there. + */ + return device_property_present(dev, "panel"); +} +EXPORT_SYMBOL(drm_is_panel_follower); + +/** + * drm_panel_add_follower() - Register something to follow panel state. + * @follower_dev: The 'struct device' for the follower. + * @follower: The panel follower descriptor for the follower. + * + * A panel follower is called right after preparing/enabling the panel and right + * before unpreparing/disabling the panel. It's primary intention is to power on + * an associated touchscreen, though it could be used for any similar devices. + * Multiple devices are allowed the follow the same panel. + * + * If a follower is added to a panel that's already been prepared/enabled, the + * follower's prepared/enabled callback is called right away. + * + * The "panel" property of the follower points to the panel to be followed. + * + * Return: 0 or an error code. Note that -ENODEV means that we detected that + * follower_dev is not actually following a panel. The caller may + * choose to ignore this return value if following a panel is optional. + */ +int drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower) +{ + struct drm_panel *panel; + int ret; + + panel = find_panel_by_dev(follower_dev); + if (IS_ERR(panel)) + return PTR_ERR(panel); + + get_device(panel->dev); + follower->panel = panel; + + mutex_lock(&panel->follower_lock); + + list_add_tail(&follower->list, &panel->followers); + if (panel->prepared && follower->funcs->panel_prepared) { + ret = follower->funcs->panel_prepared(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_prepared, ret); + } + if (panel->enabled && follower->funcs->panel_enabled) { + ret = follower->funcs->panel_enabled(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_enabled, ret); + } + + mutex_unlock(&panel->follower_lock); + + return 0; +} +EXPORT_SYMBOL(drm_panel_add_follower); + +/** + * drm_panel_remove_follower() - Reverse drm_panel_add_follower(). + * @follower: The panel follower descriptor for the follower. + * + * Undo drm_panel_add_follower(). This includes calling the follower's + * unpreparing/disabling function if we're removed from a panel that's currently + * prepared/enabled. + * + * Return: 0 or an error code. + */ +void drm_panel_remove_follower(struct drm_panel_follower *follower) +{ + struct drm_panel *panel = follower->panel; + int ret; + + mutex_lock(&panel->follower_lock); + + if (panel->enabled && follower->funcs->panel_disabling) { + ret = follower->funcs->panel_disabling(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_disabling, ret); + } + if (panel->prepared && follower->funcs->panel_unpreparing) { + ret = follower->funcs->panel_unpreparing(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_unpreparing, ret); + } + list_del_init(&follower->list); + + mutex_unlock(&panel->follower_lock); + + put_device(panel->dev); +} +EXPORT_SYMBOL(drm_panel_remove_follower); + +static void drm_panel_remove_follower_void(void *follower) +{ + drm_panel_remove_follower(follower); +} + +/** + * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower() + * @follower_dev: The 'struct device' for the follower. + * @follower: The panel follower descriptor for the follower. + * + * Handles calling drm_panel_remove_follower() using devm on the follower_dev. + * + * Return: 0 or an error code. + */ +int devm_drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower) +{ + int ret; + + ret = drm_panel_add_follower(follower_dev, follower); + if (ret) + return ret; + + return devm_add_action_or_reset(follower_dev, + drm_panel_remove_follower_void, follower); +} +EXPORT_SYMBOL(devm_drm_panel_add_follower); + +#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE) +/** + * drm_panel_of_backlight - use backlight device node for backlight + * @panel: DRM panel + * + * Use this function to enable backlight handling if your panel + * uses device tree and has a backlight phandle. + * + * When the panel is enabled backlight will be enabled after a + * successful call to &drm_panel_funcs.enable() + * + * When the panel is disabled backlight will be disabled before the + * call to &drm_panel_funcs.disable(). + * + * A typical implementation for a panel driver supporting device tree + * will call this function at probe time. Backlight will then be handled + * transparently without requiring any intervention from the driver. + * + * Return: 0 on success or a negative error code on failure. + */ +int drm_panel_of_backlight(struct drm_panel *panel) +{ + struct backlight_device *backlight; + + if (!panel || !panel->dev) + return -EINVAL; + + backlight = devm_of_find_backlight(panel->dev); + + if (IS_ERR(backlight)) + return PTR_ERR(backlight); + + panel->backlight = backlight; + return 0; +} +EXPORT_SYMBOL(drm_panel_of_backlight); +#endif + +MODULE_AUTHOR("Thierry Reding "); +MODULE_DESCRIPTION("DRM panel infrastructure"); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index d4dc8cb45bce..e26ead5b5e58 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -760,27 +760,32 @@ static void output_poll_execute(struct work_struct *work) struct drm_connector *connector; struct drm_connector_list_iter conn_iter; enum drm_connector_status old_status; - bool repoll = false, changed; + bool repoll = false, changed = false; u64 old_epoch_counter; if (!dev->mode_config.poll_enabled) return; - /* Pick up any changes detected by the probe functions. */ - changed = dev->mode_config.delayed_event; - dev->mode_config.delayed_event = false; - if (!drm_kms_helper_poll) { if (dev->mode_config.poll_running) { drm_kms_helper_disable_hpd(dev); dev->mode_config.poll_running = false; } - goto out; + + scoped_guard(mutex, &dev->mode_config.mutex) { + changed = dev->mode_config.delayed_event; + dev->mode_config.delayed_event = false; + } + + if (changed) + drm_kms_helper_hotplug_event(dev); + + return; } if (!mutex_trylock(&dev->mode_config.mutex)) { - repoll = true; - goto out; + schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); + return; } drm_connector_list_iter_begin(dev, &conn_iter); @@ -836,16 +841,23 @@ static void output_poll_execute(struct work_struct *work) connector->base.id, connector->name, old_epoch_counter, connector->epoch_counter); + drm_sysfs_connector_hotplug_event(connector); changed = true; } } drm_connector_list_iter_end(&conn_iter); + /* Pick up any changes detected by the probe functions. */ + if (dev->mode_config.delayed_event) { + dev->mode_config.delayed_event = false; + changed = true; + drm_sysfs_hotplug_event(dev); + } + mutex_unlock(&dev->mode_config.mutex); -out: if (changed) - drm_kms_helper_hotplug_event(dev); + drm_client_dev_hotplug(dev); if (repoll) schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); @@ -1081,9 +1093,9 @@ EXPORT_SYMBOL(drm_connector_helper_hpd_irq_event); */ bool drm_helper_hpd_irq_event(struct drm_device *dev) { - struct drm_connector *connector, *first_changed_connector = NULL; struct drm_connector_list_iter conn_iter; - int changed = 0; + struct drm_connector *connector; + bool changed = false; if (!dev->mode_config.poll_enabled) return false; @@ -1096,24 +1108,15 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) continue; if (check_connector_changed(connector)) { - if (!first_changed_connector) { - drm_connector_get(connector); - first_changed_connector = connector; - } - - changed++; + changed = true; + drm_sysfs_connector_hotplug_event(connector); } } drm_connector_list_iter_end(&conn_iter); mutex_unlock(&dev->mode_config.mutex); - if (changed == 1) - drm_kms_helper_connector_hotplug_event(first_changed_connector); - else if (changed > 0) - drm_kms_helper_hotplug_event(dev); - - if (first_changed_connector) - drm_connector_put(first_changed_connector); + if (changed) + drm_client_dev_hotplug(dev); return changed; } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 4dbc8a7ea6af..5c23182f4d33 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -545,13 +545,13 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu) u32 pulse_eater = 0x01590880; /* disable clock gating */ - gpu_write_power(gpu, VIVS_PM_POWER_CONTROLS, 0x0); + gpu_write_power_sync(gpu, VIVS_PM_POWER_CONTROLS, 0x0); /* disable pulse eater */ pulse_eater |= BIT(17); gpu_write_power(gpu, VIVS_PM_PULSE_EATER, pulse_eater); pulse_eater |= BIT(0); - gpu_write_power(gpu, VIVS_PM_PULSE_EATER, pulse_eater); + gpu_write_power_sync(gpu, VIVS_PM_PULSE_EATER, pulse_eater); /* enable clock */ control = VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale); @@ -662,7 +662,7 @@ static void etnaviv_gpu_enable_mlcg(struct etnaviv_gpu *gpu) gpu->identity.revision == 0x4302) ppc |= VIVS_PM_POWER_CONTROLS_DISABLE_STALL_MODULE_CLOCK_GATING; - gpu_write_power(gpu, VIVS_PM_POWER_CONTROLS, ppc); + gpu_write_power_sync(gpu, VIVS_PM_POWER_CONTROLS, ppc); pmc = gpu_read_power(gpu, VIVS_PM_MODULE_CONTROLS); @@ -706,7 +706,7 @@ static void etnaviv_gpu_enable_mlcg(struct etnaviv_gpu *gpu) pmc |= VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_RA_HZ; pmc |= VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_RA_EZ; - gpu_write_power(gpu, VIVS_PM_MODULE_CONTROLS, pmc); + gpu_write_power_sync(gpu, VIVS_PM_MODULE_CONTROLS, pmc); } void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch) @@ -772,7 +772,7 @@ static void etnaviv_gpu_setup_pulse_eater(struct etnaviv_gpu *gpu) pulse_eater |= BIT(18); } - gpu_write_power(gpu, VIVS_PM_PULSE_EATER, pulse_eater); + gpu_write_power_sync(gpu, VIVS_PM_PULSE_EATER, pulse_eater); } static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h index 5cb46c84e03a..e89d591d7404 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h @@ -204,6 +204,12 @@ static inline u32 gpu_read_power(struct etnaviv_gpu *gpu, u32 reg) return readl(gpu->mmio + gpu_fix_power_address(gpu, reg)); } +static inline void gpu_write_power_sync(struct etnaviv_gpu *gpu, u32 reg, u32 data) +{ + gpu_write_power(gpu, reg, data); + gpu_read_power(gpu, reg); +} + int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value); int etnaviv_gpu_init(struct etnaviv_gpu *gpu); diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index b80540328150..a418011f7d4d 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -182,8 +182,15 @@ static int exynos_dp_probe(struct platform_device *pdev) out: dp->adp = analogix_dp_probe(dev, &dp->plat_data); - if (IS_ERR(dp->adp)) + if (IS_ERR(dp->adp)) { + /* + * The driver core does not invoke remove() for failed probes, + * so release the probe-time panel reference here. + */ + if (dp->plat_data.panel) + drm_panel_put(dp->plat_data.panel); return PTR_ERR(dp->adp); + } if (dp->plat_data.panel || dp->plat_data.next_bridge) return component_add(&pdev->dev, &exynos_dp_ops); @@ -193,6 +200,16 @@ static int exynos_dp_probe(struct platform_device *pdev) static void exynos_dp_remove(struct platform_device *pdev) { + struct exynos_dp_device *dp = platform_get_drvdata(pdev); + + /* + * Release the probe-time reference from of_drm_find_panel(). If bind + * ran, the panel_bridge holds a second reference that devm cleanup + * will release when the bridge is destroyed after remove() returns. + */ + if (dp->plat_data.panel) + drm_panel_put(dp->plat_data.panel); + component_del(&pdev->dev, &exynos_dp_ops); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 0dc36df6ada3..9d15a0035ea9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -245,5 +245,8 @@ int exynos_dpi_remove(struct drm_encoder *encoder) exynos_dpi_disable(&ctx->encoder); + if (ctx->panel) + drm_panel_put(ctx->panel); + return 0; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index 84eff7519e32..ec71fbbb0eb8 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -109,6 +109,13 @@ static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev, return ret; } +static void fsl_dcu_panel_put_action(void *data) +{ + struct drm_panel *panel = data; + + drm_panel_put(panel); +} + int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) { struct device_node *panel_node; @@ -124,6 +131,12 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) if (IS_ERR(fsl_dev->connector.panel)) return PTR_ERR(fsl_dev->connector.panel); + ret = devm_add_action_or_reset(fsl_dev->dev, + fsl_dcu_panel_put_action, + fsl_dev->connector.panel); + if (ret) + return ret; + return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel); } @@ -132,6 +145,11 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) return ret; if (panel) { + ret = devm_add_action_or_reset(fsl_dev->dev, + fsl_dcu_panel_put_action, panel); + if (ret) + return ret; + fsl_dev->connector.panel = panel; return fsl_dcu_attach_panel(fsl_dev, panel); } diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c index 342a57c82846..e51a965ddeb8 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_crt.c +++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c @@ -29,8 +29,8 @@ #include #include +#include #include -#include #include "cdv_device.h" #include "intel_bios.h" @@ -217,6 +217,10 @@ static int cdv_intel_crt_set_property(struct drm_connector *connector, * Routines for controlling stuff on the analog port */ +static const struct drm_encoder_funcs cdv_intel_crt_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = { .dpms = cdv_intel_crt_dpms, .prepare = gma_encoder_prepare, @@ -275,7 +279,8 @@ void cdv_intel_crt_init(struct drm_device *dev, goto err_ddc_destroy; encoder = &gma_encoder->base; - ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC); + ret = drm_encoder_init(dev, encoder, &cdv_intel_crt_funcs, + DRM_MODE_ENCODER_DAC, NULL); if (ret) goto err_connector_cleanup; diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index fbed35cf7603..8d0626ddf1c3 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -33,9 +33,9 @@ #include #include #include +#include #include #include -#include #include "gma_display.h" #include "psb_drv.h" @@ -232,16 +232,17 @@ i2c_dp_aux_add_bus(struct i2c_adapter *adapter) } #define _wait_for(COND, MS, W) ({ \ - unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ - int ret__ = 0; \ - while (! (COND)) { \ - if (time_after(jiffies, timeout__)) { \ - ret__ = -ETIMEDOUT; \ - break; \ - } \ - if (W && !in_dbg_master()) msleep(W); \ - } \ - ret__; \ + unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ + int ret__ = 0; \ + while (!(COND)) { \ + if (time_after(jiffies, timeout__)) { \ + ret__ = -ETIMEDOUT; \ + break; \ + } \ + if (W && !in_dbg_master()) \ + msleep(W); \ + } \ + ret__; \ }) #define wait_for(COND, MS) _wait_for(COND, MS, 1) @@ -296,10 +297,10 @@ static struct ddi_regoff ddi_DP_train_table[] = { }; static uint32_t dp_vswing_premph_table[] = { - 0x55338954, 0x4000, - 0x554d8954, 0x2000, - 0x55668954, 0, - 0x559ac0d4, 0x6000, + 0x55338954, 0x4000, + 0x554d8954, 0x2000, + 0x55668954, 0, + 0x559ac0d4, 0x6000, }; /** * is_edp - is the given port attached to an eDP panel (either CPU or PCH) @@ -1141,7 +1142,7 @@ static void cdv_intel_dp_prepare(struct drm_encoder *encoder) cdv_intel_edp_backlight_off(intel_encoder); cdv_intel_edp_panel_off(intel_encoder); cdv_intel_edp_panel_vdd_on(intel_encoder); - } + } /* Wake up the sink first */ cdv_intel_dp_sink_dpms(intel_encoder, DRM_MODE_DPMS_ON); cdv_intel_dp_link_down(intel_encoder); @@ -1183,7 +1184,7 @@ cdv_intel_dp_dpms(struct drm_encoder *encoder, int mode) cdv_intel_edp_panel_off(intel_encoder); } } else { - if (edp) + if (edp) cdv_intel_edp_panel_on(intel_encoder); cdv_intel_dp_sink_dpms(intel_encoder, mode); if (!(dp_reg & DP_PORT_EN)) { @@ -1191,7 +1192,7 @@ cdv_intel_dp_dpms(struct drm_encoder *encoder, int mode) cdv_intel_dp_complete_link_train(intel_encoder); } if (edp) - cdv_intel_edp_backlight_on(intel_encoder); + cdv_intel_edp_backlight_on(intel_encoder); } } @@ -1419,8 +1420,8 @@ cdv_intel_dp_set_vswing_premph(struct gma_encoder *encoder, uint8_t signal_level DRM_DEBUG_KMS("Test2\n"); //return ; cdv_sb_reset(dev); - /* ;Swing voltage programming - ;gfx_dpio_set_reg(0xc058, 0x0505313A) */ + /* ;Swing voltage programming */ + /* ;gfx_dpio_set_reg(0xc058, 0x0505313A) */ cdv_sb_write(dev, ddi_reg->VSwing5, 0x0505313A); /* ;gfx_dpio_set_reg(0x8154, 0x43406055) */ @@ -1575,7 +1576,7 @@ cdv_intel_dp_complete_link_train(struct gma_encoder *encoder) intel_dp->train_set[0], intel_dp->link_configuration[0], intel_dp->link_configuration[1]); - /* channel eq pattern */ + /* channel eq pattern */ if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_2)) { @@ -1704,7 +1705,7 @@ cdv_intel_dp_detect(struct drm_connector *connector, bool force) if (edp) cdv_intel_edp_panel_vdd_off(encoder); return status; - } + } if (intel_dp->force_audio) { intel_dp->has_audio = intel_dp->force_audio > 0; @@ -1872,6 +1873,10 @@ cdv_intel_dp_destroy(struct drm_connector *connector) kfree(gma_connector); } +static const struct drm_encoder_funcs cdv_intel_dp_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs cdv_intel_dp_helper_funcs = { .dpms = cdv_intel_dp_dpms, .mode_fixup = cdv_intel_dp_mode_fixup, @@ -1956,12 +1961,12 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev gma_encoder = kzalloc_obj(struct gma_encoder); if (!gma_encoder) return; - gma_connector = kzalloc_obj(struct gma_connector); - if (!gma_connector) - goto err_connector; + gma_connector = kzalloc_obj(struct gma_connector); + if (!gma_connector) + goto err_connector; intel_dp = kzalloc_obj(struct cdv_intel_dp); if (!intel_dp) - goto err_priv; + goto err_priv; if ((output_reg == DP_C) && cdv_intel_dpc_is_edp(dev)) type = DRM_MODE_CONNECTOR_eDP; @@ -1970,13 +1975,14 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev encoder = &gma_encoder->base; drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, type); - drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS); + drm_encoder_init(dev, encoder, &cdv_intel_dp_funcs, + DRM_MODE_ENCODER_TMDS, NULL); gma_connector_attach_encoder(gma_connector, gma_encoder); if (type == DRM_MODE_CONNECTOR_DisplayPort) gma_encoder->type = INTEL_OUTPUT_DISPLAYPORT; - else + else gma_encoder->type = INTEL_OUTPUT_EDP; @@ -2006,18 +2012,18 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev cdv_disable_intel_clock_gating(dev); cdv_intel_dp_i2c_init(gma_connector, gma_encoder, name); - /* FIXME:fail check */ + /* FIXME:fail check */ cdv_intel_dp_add_properties(connector); if (is_edp(gma_encoder)) { int ret; struct edp_power_seq cur; - u32 pp_on, pp_off, pp_div; + u32 pp_on, pp_off, pp_div; u32 pwm_ctrl; pp_on = REG_READ(PP_CONTROL); pp_on &= ~PANEL_UNLOCK_MASK; - pp_on |= PANEL_UNLOCK_REGS; + pp_on |= PANEL_UNLOCK_REGS; REG_WRITE(PP_CONTROL, pp_on); @@ -2025,42 +2031,42 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev pwm_ctrl |= PWM_PIPE_B; REG_WRITE(BLC_PWM_CTL2, pwm_ctrl); - pp_on = REG_READ(PP_ON_DELAYS); - pp_off = REG_READ(PP_OFF_DELAYS); - pp_div = REG_READ(PP_DIVISOR); + pp_on = REG_READ(PP_ON_DELAYS); + pp_off = REG_READ(PP_OFF_DELAYS); + pp_div = REG_READ(PP_DIVISOR); /* Pull timing values out of registers */ - cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >> - PANEL_POWER_UP_DELAY_SHIFT; + cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >> + PANEL_POWER_UP_DELAY_SHIFT; - cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >> - PANEL_LIGHT_ON_DELAY_SHIFT; + cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >> + PANEL_LIGHT_ON_DELAY_SHIFT; - cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >> - PANEL_LIGHT_OFF_DELAY_SHIFT; + cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >> + PANEL_LIGHT_OFF_DELAY_SHIFT; - cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >> - PANEL_POWER_DOWN_DELAY_SHIFT; + cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >> + PANEL_POWER_DOWN_DELAY_SHIFT; - cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >> - PANEL_POWER_CYCLE_DELAY_SHIFT); + cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >> + PANEL_POWER_CYCLE_DELAY_SHIFT); - DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", - cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); + DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); intel_dp->panel_power_up_delay = cur.t1_t3 / 10; - intel_dp->backlight_on_delay = cur.t8 / 10; - intel_dp->backlight_off_delay = cur.t9 / 10; - intel_dp->panel_power_down_delay = cur.t10 / 10; - intel_dp->panel_power_cycle_delay = (cur.t11_t12 - 1) * 100; + intel_dp->backlight_on_delay = cur.t8 / 10; + intel_dp->backlight_off_delay = cur.t9 / 10; + intel_dp->panel_power_down_delay = cur.t10 / 10; + intel_dp->panel_power_cycle_delay = (cur.t11_t12 - 1) * 100; - DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", - intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, - intel_dp->panel_power_cycle_delay); + DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", + intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, + intel_dp->panel_power_cycle_delay); - DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", - intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); + DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", + intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); cdv_intel_edp_panel_vdd_on(gma_encoder); @@ -2075,7 +2081,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev cdv_intel_dp_destroy(connector); goto err_connector; } else { - DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n", + DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n", intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]); @@ -2083,7 +2089,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev /* The CDV reference driver moves pnale backlight setup into the displays that have a backlight: this is a good idea and one we should probably adopt, however we need to migrate all the drivers before we can do that */ - /*cdv_intel_panel_setup_backlight(dev); */ + /*cdv_intel_panel_setup_backlight(dev); */ } return; diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index ce7850647778..757ff9408250 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -30,9 +30,9 @@ #include #include #include +#include #include #include -#include #include "cdv_device.h" #include "psb_drv.h" @@ -251,6 +251,10 @@ static void cdv_hdmi_destroy(struct drm_connector *connector) kfree(gma_connector); } +static const struct drm_encoder_funcs cdv_hdmi_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs cdv_hdmi_helper_funcs = { .dpms = cdv_hdmi_dpms, .prepare = gma_encoder_prepare, @@ -329,8 +333,8 @@ void cdv_hdmi_init(struct drm_device *dev, if (ret) goto err_ddc_destroy; - ret = drm_simple_encoder_init(dev, &gma_encoder->base, - DRM_MODE_ENCODER_TMDS); + ret = drm_encoder_init(dev, &gma_encoder->base, &cdv_hdmi_funcs, + DRM_MODE_ENCODER_TMDS, NULL); if (ret) goto err_connector_cleanup; diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index d7fd9a783cde..336ab411d699 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -13,9 +13,9 @@ #include #include +#include #include #include -#include #include "cdv_device.h" #include "intel_bios.h" @@ -394,6 +394,10 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector, return 0; } +static const struct drm_encoder_funcs cdv_intel_lvds_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs cdv_intel_lvds_helper_funcs = { .dpms = cdv_intel_lvds_encoder_dpms, @@ -535,7 +539,8 @@ void cdv_intel_lvds_init(struct drm_device *dev, if (ret) goto err_destroy_ddc; - ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); + ret = drm_encoder_init(dev, encoder, &cdv_intel_lvds_funcs, + DRM_MODE_ENCODER_LVDS, NULL); if (ret) goto err_connector_cleanup; diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c index 403d21cbb3a2..5a6132176086 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c @@ -29,9 +29,9 @@ #include #include #include +#include #include #include -#include #include "psb_drv.h" #include "psb_intel_drv.h" @@ -605,6 +605,10 @@ static void oaktrail_hdmi_destroy(struct drm_connector *connector) return; } +static const struct drm_encoder_funcs oaktrail_hdmi_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs oaktrail_hdmi_helper_funcs = { .dpms = oaktrail_hdmi_dpms, .prepare = gma_encoder_prepare, @@ -648,7 +652,8 @@ void oaktrail_hdmi_init(struct drm_device *dev, &oaktrail_hdmi_connector_funcs, DRM_MODE_CONNECTOR_DVID); - drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS); + drm_encoder_init(dev, encoder, &oaktrail_hdmi_funcs, + DRM_MODE_ENCODER_TMDS, NULL); gma_connector_attach_encoder(gma_connector, gma_encoder); diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index e194d0cce067..425796d8d6c3 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -12,9 +12,9 @@ #include #include +#include #include #include -#include #include "intel_bios.h" #include "power.h" @@ -202,6 +202,10 @@ static void oaktrail_lvds_commit(struct drm_encoder *encoder) oaktrail_lvds_set_power(dev, gma_encoder, true); } +static const struct drm_encoder_funcs oaktrail_lvds_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = { .dpms = oaktrail_lvds_dpms, .mode_fixup = psb_intel_lvds_mode_fixup, @@ -319,7 +323,8 @@ void oaktrail_lvds_init(struct drm_device *dev, if (ret) goto err_free_connector; - ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); + ret = drm_encoder_init(dev, encoder, &oaktrail_lvds_funcs, + DRM_MODE_ENCODER_LVDS, NULL); if (ret) goto err_connector_cleanup; diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index 2ca164b21293..cd882786d946 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -12,9 +12,9 @@ #include #include +#include #include #include -#include #include "intel_bios.h" #include "power.h" @@ -593,6 +593,10 @@ int psb_intel_lvds_set_property(struct drm_connector *connector, return -1; } +static const struct drm_encoder_funcs psb_intel_lvds_funcs = { + .destroy = drm_encoder_cleanup, +}; + static const struct drm_encoder_helper_funcs psb_intel_lvds_helper_funcs = { .dpms = psb_intel_lvds_encoder_dpms, .mode_fixup = psb_intel_lvds_mode_fixup, @@ -679,7 +683,8 @@ void psb_intel_lvds_init(struct drm_device *dev, if (ret) goto err_ddc_destroy; - ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); + ret = drm_encoder_init(dev, encoder, &psb_intel_lvds_funcs, + DRM_MODE_ENCODER_LVDS, NULL); if (ret) goto err_connector_cleanup; diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c index 20f35c48c0b8..0efd0593d031 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c @@ -51,11 +51,12 @@ static void hv_drm_pci_remove(struct pci_dev *pdev) static const struct pci_device_id hv_drm_pci_tbl[] = { { - .vendor = PCI_VENDOR_ID_MICROSOFT, - .device = PCI_DEVICE_ID_HYPERV_VIDEO, + PCI_VDEVICE_SUB(MICROSOFT, PCI_DEVICE_ID_HYPERV_VIDEO, + 0, 0), }, { /* end of list */ } }; +MODULE_DEVICE_TABLE(pci, hv_drm_pci_tbl); /* * PCI stub to support gen1 VM. @@ -224,6 +225,7 @@ static const struct hv_vmbus_device_id hv_drm_vmbus_tbl[] = { {HV_SYNTHVID_GUID}, {} }; +MODULE_DEVICE_TABLE(vmbus, hv_drm_vmbus_tbl); static struct hv_driver hv_drm_hv_driver = { .name = KBUILD_MODNAME, @@ -249,7 +251,11 @@ static int __init hv_drm_init(void) if (ret != 0) return ret; - return vmbus_driver_register(&hv_drm_hv_driver); + ret = vmbus_driver_register(&hv_drm_hv_driver); + if (ret) + pci_unregister_driver(&hv_drm_pci_driver); + + return ret; } static void __exit hv_drm_exit(void) @@ -261,8 +267,6 @@ static void __exit hv_drm_exit(void) module_init(hv_drm_init); module_exit(hv_drm_exit); -MODULE_DEVICE_TABLE(pci, hv_drm_pci_tbl); -MODULE_DEVICE_TABLE(vmbus, hv_drm_vmbus_tbl); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Deepak Rawat "); MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device"); diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/imagination/pvr_device.c index 2691ef9af0ca..54fe4180c73c 100644 --- a/drivers/gpu/drm/imagination/pvr_device.c +++ b/drivers/gpu/drm/imagination/pvr_device.c @@ -531,12 +531,10 @@ pvr_gpu_support_level(const struct pvr_gpu_id *gpu_id) { switch (pvr_gpu_id_to_packed_bvnc(gpu_id)) { case PVR_PACKED_BVNC(33, 15, 11, 3): + case PVR_PACKED_BVNC(36, 52, 104, 182): case PVR_PACKED_BVNC(36, 53, 104, 796): return PVR_GPU_SUPPORTED; - case PVR_PACKED_BVNC(36, 52, 104, 182): - return PVR_GPU_EXPERIMENTAL; - default: return PVR_GPU_UNKNOWN; } diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c index 8897b3bdeb4a..5c965ef0274f 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -43,8 +43,9 @@ * * This driver supports the following PowerVR/IMG graphics cores from Imagination Technologies: * - * * AXE-1-16M (found in Texas Instruments AM62) - * * BXS-4-64 MC1 (found in Texas Instruments J721S2/AM68) + * * AXE-1-16M (33.15.11.3) + * * BXM-4-64 MC1 (36.52.104.182) + * * BXS-4-64 MC1 (36.53.104.796) */ /** diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 941c017399fc..54e88b4208d7 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -3,6 +3,7 @@ #include #include +#include #include "pvr_cccb.h" #include "pvr_context.h" @@ -36,9 +37,8 @@ static int get_xfer_ctx_state_size(struct pvr_device *pvr_dev) return err; } - return sizeof(struct rogue_fwif_frag_ctx_state) + - (num_isp_store_registers * - sizeof(((struct rogue_fwif_frag_ctx_state *)0)->frag_reg_isp_store[0])); + return struct_size_t(struct rogue_fwif_frag_ctx_state, + frag_reg_isp_store, num_isp_store_registers); } static int get_frag_ctx_state_size(struct pvr_device *pvr_dev) @@ -66,9 +66,8 @@ static int get_frag_ctx_state_size(struct pvr_device *pvr_dev) return err; } - return sizeof(struct rogue_fwif_frag_ctx_state) + - (num_isp_store_registers * - sizeof(((struct rogue_fwif_frag_ctx_state *)0)->frag_reg_isp_store[0])); + return struct_size_t(struct rogue_fwif_frag_ctx_state, + frag_reg_isp_store, num_isp_store_registers); } static int get_ctx_state_size(struct pvr_device *pvr_dev, enum drm_pvr_job_type type) diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h index 51dc37e78f41..e72f4064af18 100644 --- a/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h @@ -5,6 +5,8 @@ #define PVR_ROGUE_FWIF_CHECK_H #include +#include +#include #define OFFSET_CHECK(type, member, offset) \ static_assert(offsetof(type, member) == (offset), \ @@ -13,6 +15,21 @@ #define SIZE_CHECK(type, size) \ static_assert(sizeof(type) == (size), #type " is incorrect size") +/* + * Where the last member of a struct is a flexible array member, using + * SIZE_CHECK() is pointless. If the structure is not already padded to + * alignment without the flexible array member, sizeof() will not match the + * offset of the flexible array member and the "correct" sizeof() value is + * completely meaningless. + * + * In those instances, use FLEX_ARRAY_CHECK() instead to assert that the final + * field is a flexible array member and that it behaves as expected. + */ +#define FLEX_ARRAY_CHECK(type, member) \ + static_assert(flex_array_size((type *)NULL, member, 1) == \ + sizeof_field(type, member[0]), \ + #type "->" #member " is incorrect size") + OFFSET_CHECK(struct rogue_fwif_file_info_buf, path, 0); OFFSET_CHECK(struct rogue_fwif_file_info_buf, info, 200); OFFSET_CHECK(struct rogue_fwif_file_info_buf, line_num, 400); @@ -157,7 +174,7 @@ OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_pm_deallocated_mask_stat OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_dm_pds_mtilefree_status, 4); OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, ctx_state_flags, 8); OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_isp_store, 12); -SIZE_CHECK(struct rogue_fwif_frag_ctx_state, 16); +FLEX_ARRAY_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_isp_store); OFFSET_CHECK(struct rogue_fwif_compute_ctx_state, ctx_state_flags, 0); SIZE_CHECK(struct rogue_fwif_compute_ctx_state, 4); diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c index 50bd7f36d36d..7c5a6e2bc89a 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-kms.c +++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -77,6 +78,9 @@ static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms) if (ret) return ret; + if (panel) + drm_panel_put(panel); + if (!bridge) { dev_err(ddev->dev, "No bridge found %d.\n", ret); return -ENODEV; diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 7e569af22391..738a80b2550f 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -1297,9 +1297,11 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) goto err_drvdata; } - if (panel) + if (panel) { bridge = devm_drm_panel_bridge_add_typed(dev, panel, DRM_MODE_CONNECTOR_DPI); + drm_panel_put(panel); + } ib = drmm_encoder_alloc(drm, struct ingenic_drm_bridge, encoder, NULL, DRM_MODE_ENCODER_DPI, NULL); diff --git a/drivers/gpu/drm/logicvc/logicvc_interface.c b/drivers/gpu/drm/logicvc/logicvc_interface.c index 689049d395c0..81f760dc07f8 100644 --- a/drivers/gpu/drm/logicvc/logicvc_interface.c +++ b/drivers/gpu/drm/logicvc/logicvc_interface.c @@ -28,6 +28,11 @@ #define logicvc_interface_from_drm_connector(c) \ container_of(c, struct logicvc_interface, drm_connector) +static void logicvc_panel_put_action(void *data) +{ + drm_panel_put(data); +} + static void logicvc_encoder_enable(struct drm_encoder *drm_encoder) { struct logicvc_drm *logicvc = logicvc_drm(drm_encoder->dev); @@ -160,6 +165,13 @@ int logicvc_interface_init(struct logicvc_drm *logicvc) if (ret == -EPROBE_DEFER) goto error_early; + if (interface->drm_panel) { + ret = devm_add_action_or_reset(dev, logicvc_panel_put_action, + interface->drm_panel); + if (ret) + goto error_early; + } + ret = drm_encoder_init(drm_dev, &interface->drm_encoder, &logicvc_encoder_funcs, encoder_type, NULL); if (ret) { diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index 5f2c462bad7e..53275b575f0c 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -153,6 +153,7 @@ static int mcde_modeset_init(struct drm_device *drm) if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DPI); + drm_panel_put(panel); if (IS_ERR(bridge)) { dev_err(drm->dev, "Could not connect panel bridge\n"); diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index 5cf44ccb02cf..694372581840 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -1127,6 +1127,7 @@ static int mcde_dsi_bind(struct device *dev, struct device *master, if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DSI); + drm_panel_put(panel); if (IS_ERR(bridge)) { dev_err(dev, "error adding panel bridge\n"); return PTR_ERR(bridge); diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 9b8fbda85d28..8b4a500347fb 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -127,6 +128,7 @@ static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) if (panel) { bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel, DRM_MODE_CONNECTOR_DPI); + drm_panel_put(panel); if (IS_ERR(bridge)) return PTR_ERR(bridge); } diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 7ea95ab960a0..4d1ad718e09b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1209,7 +1209,6 @@ nouveau_pmops_runtime_idle(struct device *dev) return -EBUSY; } - pm_runtime_mark_last_busy(dev); pm_runtime_autosuspend(dev); /* we don't want the main rpm_idle to call suspend - we want to autosuspend */ return 1; diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index ca891aba3820..6e9bc605ee22 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -43,6 +43,7 @@ int omapdss_device_init_output(struct omap_dss_device *out, struct drm_bridge *bridge; bridge = drm_panel_bridge_add(out->panel); + drm_panel_put(out->panel); if (IS_ERR(bridge)) { dev_err(out->dev, "unable to create panel bridge (%ld)\n", diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index cfbfb371bc67..53c0b95c26ca 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -662,6 +662,17 @@ config DRM_PANEL_NOVATEK_NT36523 around the Novatek NT36523 display controller, such as some Boe panels used in Xiaomi Mi Pad 5 and 5 Pro tablets. +config DRM_PANEL_NOVATEK_NT36536 + tristate "Novatek NT36536 panel driver" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select DRM_KMS_HELPER + help + Say Y here if you want to enable support for Novatek NT36536-based + display panels, such as the one found in the LENOVO Legion Y700 + Gen4. + config DRM_PANEL_NOVATEK_NT36672A tristate "Novatek NT36672A DSI panel" depends on GPIOLIB diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 0f29f22f589e..3b523cf37833 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35532) += panel-novatek-nt35532.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36523) += panel-novatek-nt36523.o +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36536) += panel-novatek-nt36536.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) += panel-novatek-nt36672e.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37700F) += panel-novatek-nt37700f.o diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index f66474d3ef65..df7286679446 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1936,7 +1936,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x145c, &delay_200_500_e50, "B116XAB01.4"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x1999, &delay_200_500_e50, "Unknown"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x1e9b, &delay_200_500_e50, "B133UAN02.1"), - EDP_PANEL_ENTRY('A', 'U', 'O', 0x1ea5, &delay_200_500_e50, "B116XAK01.6"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x1ea5, &delay_200_500_e200, "B116XAK01.6"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x203d, &delay_200_500_e50, "B140HTN02.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x205c, &delay_200_500_e50, "B116XAN02.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x208d, &delay_200_500_e50, "B140HTN02.1"), @@ -1958,7 +1958,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x635c, &delay_200_500_e50, "B116XAN06.3"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x639c, &delay_200_500_e50, "B140HAK02.7"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x643d, &delay_200_500_e50, "B140HAN06.4"), - EDP_PANEL_ENTRY('A', 'U', 'O', 0x67a8, &delay_200_500_e50, "B140XTK02.4"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x67a8, &delay_200_500_e200, "B140XTK02.4"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x723c, &delay_200_500_e50, "B140XTN07.2"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x73aa, &delay_200_500_e50, "B116XTN02.3"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x8594, &delay_200_500_e50, "B133UAN01.0"), diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36536.c b/drivers/gpu/drm/panel/panel-novatek-nt36536.c new file mode 100644 index 000000000000..2a82b54880c3 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-novatek-nt36536.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Novatek NT36536 DriverIC panels driver + * Copyright (c) 2026 Pengyu Luo + * + * Based on the sample code which is generated with + * linux-mdss-dsi-panel-driver-generator + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include