Merge tag 'drm-misc-next-2026-07-24' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next

drm-misc-next for v7.3:

UAPI Changes:

cgroup:
- dmem: accept one region per limit

drm:
- send per-connector hotplug events

Core Changes:

buddy:
- tests: test buffer clearence on resume

panel:
- implement ref counting for struct drm_panel throughout the DRM tree

Driver Changes:

etnaviv:
- force flush on power register ops

gma:
- replace simple-kms helpers with regular atomic helpers
- clean up

host1x:
- support Tegra264 plus DT bindings
- fix minor issues throughout driver

hypervdrm:
- clean up PCI-device binding

imagination:
- mark BXM-4-64 MC1 (36.52.104.182) as supported
- clean up

ivpu:
- clean up

nouveau:
- remove redundant call to pm_runtime_mark_last_busy()

panel:
- support Novatek NT36536 plus DT bindings
- sofef00: fix backlight updates
- osd101t2587: use mipi_dsi_*_multi interface
- panel-edp: adjust timing for AUO displays
- panel-lvds: support Opto Logic SCX1001511GGC49
- panel-simple: support Kyocera tcg070wvlq

panfrost:
- clean up

solomon:
- clean up variables

tve200:
- add OF module alias for autoloading

v3d:
- fix job BO handling

vc4:
- ref i2c-adapter module

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patch.msgid.link/20260724082204.GA18774@linux.fritz.box
This commit is contained in:
Dave Airlie
2026-07-25 06:10:53 +10:00
105 changed files with 2459 additions and 302 deletions

View File

@@ -628,7 +628,8 @@ Nadav Amit <nadav.amit@gmail.com> <namit@cs.technion.ac.il>
Nadia Yvette Chambers <nyc@holomorphy.com> William Lee Irwin III <wli@holomorphy.com>
Naoya Horiguchi <nao.horiguchi@gmail.com> <n-horiguchi@ah.jp.nec.com>
Naoya Horiguchi <nao.horiguchi@gmail.com> <naoya.horiguchi@nec.com>
Natalie Vock <natalie.vock@gmx.de> <friedrich.vock@gmx.de>
Natalie Vock <nat@pixelcluster.dev> <friedrich.vock@gmx.de>
Natalie Vock <nat@pixelcluster.dev> <natalie.vock@gmx.de>
Nathan Chancellor <nathan@kernel.org> <natechancellor@gmail.com>
Naveen N Rao <naveen@kernel.org> <naveen.n.rao@linux.ibm.com>
Naveen N Rao <naveen@kernel.org> <naveen.n.rao@linux.vnet.ibm.com>

View File

@@ -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 <mitltlatltl@gmail.com>
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 <dt-bindings/gpio/gpio.h>
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>;
};
};
};
};
};
...

View File

@@ -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

View File

@@ -22,6 +22,7 @@ properties:
- nvidia,tegra186-vic
- nvidia,tegra194-vic
- nvidia,tegra234-vic
- nvidia,tegra264-vic
- items:
- const: nvidia,tegra132-vic

View File

@@ -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:
- |

View File

@@ -6636,7 +6636,7 @@ F: tools/testing/selftests/cgroup/test_cpuset_v1_base.sh
CONTROL GROUP - DEVICE MEMORY CONTROLLER (DMEM)
M: Maarten Lankhorst <dev@lankhorst.se>
M: Maxime Ripard <mripard@kernel.org>
M: Natalie Vock <natalie.vock@gmx.de>
M: Natalie Vock <nat@pixelcluster.dev>
L: cgroups@vger.kernel.org
L: dri-devel@lists.freedesktop.org
S: Maintained

View File

@@ -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;

View File

@@ -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);

View File

@@ -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)) {

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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 <linux/backlight.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/of.h>
#include <drm/drm_crtc.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
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 <treding@nvidia.com>");
MODULE_DESCRIPTION("DRM panel infrastructure");
MODULE_LICENSE("GPL and additional rights");

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -29,8 +29,8 @@
#include <linux/pm_runtime.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_simple_kms_helper.h>
#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;

View File

@@ -33,9 +33,9 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#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;

View File

@@ -30,9 +30,9 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#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;

View File

@@ -13,9 +13,9 @@
#include <linux/pm_runtime.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#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;

View File

@@ -29,9 +29,9 @@
#include <drm/drm.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#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);

View File

@@ -12,9 +12,9 @@
#include <linux/pm_runtime.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#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;

View File

@@ -12,9 +12,9 @@
#include <linux/pm_runtime.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#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;

View File

@@ -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 <drawat.floss@gmail.com>");
MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device");

View File

@@ -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;
}

View File

@@ -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)
*/
/**

View File

@@ -3,6 +3,7 @@
#include <drm/drm_managed.h>
#include <drm/gpu_scheduler.h>
#include <linux/overflow.h>
#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)

View File

@@ -5,6 +5,8 @@
#define PVR_ROGUE_FWIF_CHECK_H
#include <linux/build_bug.h>
#include <linux/overflow.h>
#include <linux/stddef.h>
#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);

View File

@@ -13,6 +13,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -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;

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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");

View File

@@ -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);

View File

@@ -29,6 +29,7 @@
#include <drm/drm_mode_config.h>
#include <drm/drm_module.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -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);
}

View File

@@ -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;

View File

@@ -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",

View File

@@ -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

View File

@@ -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

View File

@@ -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"),

View File

@@ -0,0 +1,488 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Novatek NT36536 DriverIC panels driver
* Copyright (c) 2026 Pengyu Luo <mitltlatltl@gmail.com>
*
* Based on the sample code which is generated with
* linux-mdss-dsi-panel-driver-generator
*/
#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/device-id/of.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <drm/display/drm_dsc.h>
#include <drm/display/drm_dsc_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
#include <video/mipi_display.h>
struct novatek {
struct drm_panel panel;
struct mipi_dsi_device *dsi[2];
const struct panel_desc *desc;
struct drm_dsc_config dsc;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data *supplies;
struct backlight_device *backlight;
};
struct panel_desc {
unsigned int width_mm;
unsigned int height_mm;
unsigned int bpc;
unsigned int lanes;
enum mipi_dsi_pixel_format format;
unsigned long mode_flags;
const struct drm_dsc_config *dsc_cfg;
const struct drm_display_mode *modes;
unsigned int num_modes;
int (*init_sequence)(struct mipi_dsi_multi_context *dsi_ctx);
bool is_dual_dsi;
bool has_dcs_backlight;
};
static const struct regulator_bulk_data novatek_supplies[] = {
{ .supply = "vddio" },
{ .supply = "vsp" },
{ .supply = "vsn" },
};
static inline struct novatek *to_novatek(struct drm_panel *panel)
{
return container_of(panel, struct novatek, panel);
}
static inline struct mipi_dsi_device *to_primary_dsi(struct novatek *ctx)
{
/* Sync on DSI1 for dual dsi */
return ctx->desc->is_dual_dsi ? ctx->dsi[1] : ctx->dsi[0];
}
static void novatek_reset(struct novatek *ctx)
{
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
usleep_range(11000, 12000);
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
usleep_range(1000, 2000);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
usleep_range(3000, 4000);
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
usleep_range(10000, 11000);
}
static int novatek_prepare(struct drm_panel *panel)
{
struct novatek *ctx = to_novatek(panel);
struct mipi_dsi_device *dsi = to_primary_dsi(ctx);
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
struct drm_dsc_picture_parameter_set pps;
struct device *dev = &dsi->dev;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(novatek_supplies),
ctx->supplies);
if (ret < 0)
return ret;
novatek_reset(ctx);
ret = ctx->desc->init_sequence(&dsi_ctx);
if (ret < 0) {
dev_err(dev, "Failed to initialize panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(novatek_supplies),
ctx->supplies);
return ret;
}
drm_dsc_pps_payload_pack(&pps, &ctx->dsc);
mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
mipi_dsi_compression_mode_multi(&dsi_ctx, true);
mipi_dsi_msleep(&dsi_ctx, 28);
return backlight_enable(ctx->backlight);
}
static int novatek_off(struct mipi_dsi_multi_context *dsi_ctx)
{
mipi_dsi_dcs_set_display_off_multi(dsi_ctx);
mipi_dsi_dcs_enter_sleep_mode_multi(dsi_ctx);
return dsi_ctx->accum_err;
}
static int novatek_unprepare(struct drm_panel *panel)
{
struct novatek *ctx = to_novatek(panel);
struct mipi_dsi_device *dsi = to_primary_dsi(ctx);
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
struct device *dev = &dsi->dev;
int ret;
backlight_disable(ctx->backlight);
ret = novatek_off(&dsi_ctx);
if (ret < 0)
dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(novatek_supplies), ctx->supplies);
return 0;
}
static int novatek_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct novatek *ctx = to_novatek(panel);
const struct panel_desc *desc = ctx->desc;
int i;
for (i = 0; i < desc->num_modes; i++) {
const struct drm_display_mode *m = &desc->modes[i];
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, m);
if (!mode) {
dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
return -ENOMEM;
}
mode->type = DRM_MODE_TYPE_DRIVER;
if (i == 0)
mode->type |= DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
}
connector->display_info.width_mm = desc->width_mm;
connector->display_info.height_mm = desc->height_mm;
connector->display_info.bpc = desc->bpc;
return desc->num_modes;
}
static const struct drm_panel_funcs novatek_panel_funcs = {
.prepare = novatek_prepare,
.unprepare = novatek_unprepare,
.get_modes = novatek_get_modes,
};
static int novatek_bl_update_status(struct backlight_device *bl)
{
struct novatek *ctx = bl_get_data(bl);
u16 brightness = backlight_get_brightness(bl);
return mipi_dsi_dcs_set_display_brightness_large(to_primary_dsi(ctx),
brightness);
}
static const struct backlight_ops novatek_bl_ops = {
.update_status = novatek_bl_update_status,
};
static struct backlight_device *novatek_create_backlight(struct novatek *ctx)
{
struct mipi_dsi_device *dsi = to_primary_dsi(ctx);
struct device *dev = &dsi->dev;
const struct backlight_properties props = {
.type = BACKLIGHT_RAW,
.brightness = 512,
.max_brightness = 4095,
.scale = BACKLIGHT_SCALE_NON_LINEAR,
};
return devm_backlight_device_register(dev, dev_name(dev), dev, ctx,
&novatek_bl_ops, &props);
}
static int csot_pp8807hb1_1_init_seq(struct mipi_dsi_multi_context *dsi_ctx)
{
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x20);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_SET_PARTIAL_ROWS, 0x50);
/* cabc */
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x23);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x89, 0xa4);
/* pen code */
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x26);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xcd, 0x8f, 0xa9, 0x00);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xce, 0x8c, 0xa9, 0x00);
/* esd init */
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x27);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x79, 0x22);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd0, 0x31);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd1, 0x08, 0x08);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd2, 0x08);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0xd0);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x00, 0x31);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x09, 0xee);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1c, 0x77);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1d, 0x07);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0xe0);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x00);
/* IC transfer */
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0xf0);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe6, 0x02);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3b,
0x03, 0x4e, 0x1a, 0x04, 0x04, 0x01, 0x80,
0x36, 0x36);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x60, 0x00);
/* enable DSC */
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x90, 0x03, 0x00);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x91,
0xab, 0xa8, 0x00, 0x14, 0xd2, 0x00, 0x00,
0x00, 0x01, 0xb9, 0x00, 0x06, 0x05, 0x7a,
0x05, 0xb8);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x92, 0x10, 0xe0);
/* frame ctl */
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x91, 0x80);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb3, 0x40);
mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx);
mipi_dsi_msleep(dsi_ctx, 105);
mipi_dsi_dcs_set_display_on_multi(dsi_ctx);
mipi_dsi_msleep(dsi_ctx, 40);
return dsi_ctx->accum_err;
}
static struct drm_dsc_config csot_pp8807hb1_1_dsc_cfg = {
.dsc_version_major = 1,
.dsc_version_minor = 2,
.slice_height = 20,
.slice_width = 476,
.slice_count = 2,
.bits_per_component = 10,
.bits_per_pixel = 8 << 4,
.block_pred_enable = true,
};
static const struct drm_display_mode csot_pp8807hb1_1_modes[] = {
/* 915552 KHz */
{
.clock = (952 + 138 + 16 + 16) * 2 * (3040 + 26 + 4 + 330) * 120 / 1000,
.hdisplay = 952 * 2,
.hsync_start = (952 + 138) * 2,
.hsync_end = (952 + 138 + 16) * 2,
.htotal = (952 + 138 + 16 + 16) * 2,
.vdisplay = 3040,
.vsync_start = 3040 + 26,
.vsync_end = 3040 + 26 + 4,
.vtotal = 3040 + 26 + 4 + 330,
},
{
.clock = (952 + 138 + 16 + 16) * 2 * (3040 + 3426 + 4 + 330) * 60 / 1000,
.hdisplay = 952 * 2,
.hsync_start = (952 + 138) * 2,
.hsync_end = (952 + 138 + 16) * 2,
.htotal = (952 + 138 + 16 + 16) * 2,
.vdisplay = 3040,
.vsync_start = 3040 + 3426,
.vsync_end = 3040 + 3426 + 4,
.vtotal = 3040 + 3426 + 4 + 330,
},
{
.clock = (952 + 138 + 16 + 16) * 2 * (3040 + 10226 + 4 + 330) * 30 / 1000,
.hdisplay = 952 * 2,
.hsync_start = (952 + 138) * 2,
.hsync_end = (952 + 138 + 16) * 2,
.htotal = (952 + 138 + 16 + 16) * 2,
.vdisplay = 3040,
.vsync_start = 3040 + 10226,
.vsync_end = 3040 + 10226 + 4,
.vtotal = 3040 + 10226 + 4 + 330,
},
/* 1064606.4 KHz */
{
.clock = (952 + 50 + 16 + 16) * 2 * (3040 + 26 + 4 + 50) * 165 / 1000,
.hdisplay = 952 * 2,
.hsync_start = (952 + 50) * 2,
.hsync_end = (952 + 50 + 16) * 2,
.htotal = (952 + 50 + 16 + 16) * 2,
.vdisplay = 3040,
.vsync_start = 3040 + 26,
.vsync_end = 3040 + 26 + 4,
.vtotal = 3040 + 26 + 4 + 50,
},
{
.clock = (952 + 50 + 16 + 16) * 2 * (3040 + 481 + 4 + 50) * 144 / 1000,
.hdisplay = 952 * 2,
.hsync_start = (952 + 50) * 2,
.hsync_end = (952 + 50 + 16) * 2,
.htotal = (952 + 50 + 16 + 16) * 2,
.vdisplay = 3040,
.vsync_start = 3040 + 481,
.vsync_end = 3040 + 481 + 4,
.vtotal = 3040 + 481 + 4 + 50,
},
/* 737942.4 KHz */
{
.clock = (952 + 330 + 16 + 16) * 2 * (3040 + 26 + 4 + 50) * 90 / 1000,
.hdisplay = 952 * 2,
.hsync_start = (952 + 330) * 2,
.hsync_end = (952 + 330 + 16) * 2,
.htotal = (952 + 330 + 16 + 16) * 2,
.vdisplay = 3040,
.vsync_start = 3040 + 26,
.vsync_end = 3040 + 26 + 4,
.vtotal = 3040 + 26 + 4 + 50,
},
};
static int novatek_probe(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_device_info dsi_info = {"dsi-secondary", 0, NULL};
struct mipi_dsi_host *dsi1_host;
struct device *dev = &dsi->dev;
const struct panel_desc *desc;
struct device_node *dsi1;
struct novatek *ctx;
int num_dsi = 1;
int ret, i;
ctx = devm_drm_panel_alloc(dev, struct novatek, panel,
&novatek_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(novatek_supplies),
novatek_supplies, &ctx->supplies);
if (ret < 0)
return ret;
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->reset_gpio))
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
"Failed to get reset-gpios\n");
desc = of_device_get_match_data(dev);
if (!desc)
return -ENODEV;
ctx->desc = desc;
ctx->dsc = *desc->dsc_cfg;
if (desc->is_dual_dsi) {
num_dsi = 2;
dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
if (!dsi1) {
dev_err(dev, "cannot get secondary DSI node.\n");
return -ENODEV;
}
dsi1_host = of_find_mipi_dsi_host_by_node(dsi1);
of_node_put(dsi1);
if (!dsi1_host)
return dev_err_probe(dev, -EPROBE_DEFER,
"cannot get secondary DSI host\n");
ctx->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi1_host,
&dsi_info);
if (IS_ERR(ctx->dsi[1])) {
dev_err(dev, "cannot get secondary DSI device\n");
return PTR_ERR(ctx->dsi[1]);
}
mipi_dsi_set_drvdata(ctx->dsi[1], ctx);
}
ctx->dsi[0] = dsi;
mipi_dsi_set_drvdata(dsi, ctx);
ctx->panel.prepare_prev_first = true;
ret = devm_drm_panel_add(dev, &ctx->panel);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to add panel\n");
for (i = 0; i < num_dsi; i++) {
ctx->dsi[i]->lanes = desc->lanes;
ctx->dsi[i]->format = desc->format;
ctx->dsi[i]->mode_flags = desc->mode_flags;
ctx->dsi[i]->dsc = &ctx->dsc;
ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]);
if (ret < 0) {
drm_panel_remove(&ctx->panel);
return dev_err_probe(dev, ret,
"Failed to attach to DSI host\n");
}
}
if (desc->has_dcs_backlight) {
ctx->backlight = novatek_create_backlight(ctx);
if (IS_ERR(ctx->backlight))
return dev_err_probe(dev, PTR_ERR(ctx->backlight),
"Failed to create backlight\n");
} else {
ret = drm_panel_of_backlight(&ctx->panel);
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
}
return 0;
}
/* Model name: CSOT PP8807HB1-1 */
static const struct panel_desc csot_pp8807hb1_1_desc = {
.width_mm = 118,
.height_mm = 190,
.bpc = 10, /* set this to 8 with RGB888 format to support 8-bit mode */
.lanes = 3,
.format = MIPI_DSI_FMT_RGB101010,
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS |
MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT,
.dsc_cfg = &csot_pp8807hb1_1_dsc_cfg,
.modes = csot_pp8807hb1_1_modes,
.num_modes = ARRAY_SIZE(csot_pp8807hb1_1_modes),
.init_sequence = csot_pp8807hb1_1_init_seq,
.is_dual_dsi = true,
.has_dcs_backlight = false,
};
static const struct of_device_id novatek_of_match[] = {
{ .compatible = "csot,pp8807hb1-1", .data = &csot_pp8807hb1_1_desc },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, novatek_of_match);
static struct mipi_dsi_driver novatek_driver = {
.probe = novatek_probe,
.driver = {
.name = "panel-novatek-nt36536",
.of_match_table = novatek_of_match,
},
};
module_mipi_dsi_driver(novatek_driver);
MODULE_AUTHOR("Pengyu Luo <mitltlatltl@gmail.com>");
MODULE_DESCRIPTION("Novatek NT36536 DriverIC panels driver");
MODULE_LICENSE("GPL");

View File

@@ -32,11 +32,11 @@ static inline struct osd101t2587_panel *ti_osd_panel(struct drm_panel *panel)
static int osd101t2587_panel_disable(struct drm_panel *panel)
{
struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel);
int ret;
struct mipi_dsi_multi_context ctx = { .dsi = osd101t2587->dsi };
ret = mipi_dsi_shutdown_peripheral(osd101t2587->dsi);
mipi_dsi_shutdown_peripheral_multi(&ctx);
return ret;
return ctx.accum_err;
}
static int osd101t2587_panel_unprepare(struct drm_panel *panel)
@@ -58,13 +58,11 @@ static int osd101t2587_panel_prepare(struct drm_panel *panel)
static int osd101t2587_panel_enable(struct drm_panel *panel)
{
struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel);
int ret;
struct mipi_dsi_multi_context ctx = { .dsi = osd101t2587->dsi };
ret = mipi_dsi_turn_on_peripheral(osd101t2587->dsi);
if (ret)
return ret;
mipi_dsi_turn_on_peripheral_multi(&ctx);
return ret;
return ctx.accum_err;
}
static const struct drm_display_mode default_mode_osd101t2587 = {

View File

@@ -175,9 +175,13 @@ static const struct drm_panel_funcs sofef00_panel_panel_funcs = {
static int sofef00_panel_bl_update_status(struct backlight_device *bl)
{
struct mipi_dsi_device *dsi = bl_get_data(bl);
struct sofef00_panel *ctx = mipi_dsi_get_drvdata(dsi);
int err;
u16 brightness = (u16)backlight_get_brightness(bl);
if (!ctx->panel.prepared)
return 0;
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
err = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);

View File

@@ -5909,6 +5909,9 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "koe,tx31d200vm0baa",
.data = &koe_tx31d200vm0baa,
}, {
.compatible = "kyo,tcg070wvlq",
.data = &lg_lb070wv8,
}, {
.compatible = "kyo,tcg121xglp",
.data = &kyo_tcg121xglp,

View File

@@ -218,7 +218,6 @@ int panfrost_device_init(struct panfrost_device *pfdev)
int err;
mutex_init(&pfdev->sched_lock);
INIT_LIST_HEAD(&pfdev->scheduled_jobs);
INIT_LIST_HEAD(&pfdev->as_lru_list);
spin_lock_init(&pfdev->as_lock);

View File

@@ -156,7 +156,6 @@ struct panfrost_device {
struct panfrost_job_slot *js;
struct panfrost_job *jobs[NUM_JOB_SLOTS][2];
struct list_head scheduled_jobs;
struct panfrost_perfcnt *perfcnt;
bool profile_mode;

View File

@@ -145,6 +145,7 @@ static int pl111_modeset_init(struct drm_device *dev)
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_Unknown);
drm_panel_put(panel);
if (IS_ERR(bridge)) {
ret = PTR_ERR(bridge);
goto finish;

View File

@@ -69,6 +69,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
DRM_MODE_CONNECTOR_DPI);
drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(no_free_ptr(bridge));

View File

@@ -791,6 +791,7 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
if (lvds->panel) {
lvds->next_bridge = devm_drm_panel_bridge_add(lvds->dev,
lvds->panel);
drm_panel_put(lvds->panel);
if (IS_ERR_OR_NULL(lvds->next_bridge)) {
ret = -EINVAL;
goto done;

View File

@@ -90,6 +90,7 @@ int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu,
bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
DRM_MODE_CONNECTOR_DPI);
drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(no_free_ptr(bridge));

View File

@@ -28,6 +28,7 @@
#include <drm/drm_bridge_connector.h>
#include <drm/bridge/analogix_dp.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -485,6 +486,16 @@ static int rockchip_dp_probe(struct platform_device *pdev)
static void rockchip_dp_remove(struct platform_device *pdev)
{
struct rockchip_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, &rockchip_dp_component_ops);
}

View File

@@ -610,6 +610,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
if (lvds->panel) {
lvds->bridge = drm_panel_bridge_add_typed(lvds->panel, DRM_MODE_CONNECTOR_LVDS);
drm_panel_put(lvds->panel);
lvds->panel = NULL;
if (IS_ERR(lvds->bridge)) {
ret = PTR_ERR(lvds->bridge);
goto err_free_encoder;
@@ -646,6 +648,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
err_free_encoder:
drm_encoder_cleanup(encoder);
err_put_remote:
if (lvds->panel)
drm_panel_put(lvds->panel);
of_node_put(remote);
err_put_port:
of_node_put(port);

View File

@@ -135,6 +135,8 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
if (ret < 0) {
DRM_DEV_ERROR(drm_dev->dev,
"failed to initialize encoder: %d\n", ret);
if (panel)
drm_panel_put(panel);
return ERR_PTR(ret);
}
@@ -143,6 +145,7 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_LVDS);
drm_panel_put(panel);
if (IS_ERR(bridge))
return ERR_CAST(bridge);
}

View File

@@ -897,7 +897,6 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb,
struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev);
struct iosys_map dst;
unsigned int dst_pitch;
int ret = 0;
/* Align y to display page boundaries */
rect->y1 = round_down(rect->y1, SSD130X_PAGE_HEIGHT);
@@ -910,7 +909,7 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb,
ssd130x_update_rect(ssd130x, rect, buf, data_array);
return ret;
return 0;
}
static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb,
@@ -922,7 +921,6 @@ static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb,
struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev);
unsigned int dst_pitch;
struct iosys_map dst;
int ret = 0;
/* Align x to display segment boundaries */
rect->x1 = round_down(rect->x1, SSD132X_SEGMENT_WIDTH);
@@ -936,7 +934,7 @@ static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb,
ssd132x_update_rect(ssd130x, rect, buf, data_array);
return ret;
return 0;
}
static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb,
@@ -948,7 +946,6 @@ static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb,
const struct drm_format_info *fi = drm_format_info(DRM_FORMAT_RGB332);
unsigned int dst_pitch;
struct iosys_map dst;
int ret = 0;
if (!fi)
return -EINVAL;
@@ -960,7 +957,7 @@ static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb,
ssd133x_update_rect(ssd130x, rect, data_array, dst_pitch);
return ret;
return 0;
}
static int ssd130x_primary_plane_atomic_check(struct drm_plane *plane,

View File

@@ -498,6 +498,9 @@ static void sti_dvo_unbind(struct device *dev,
{
struct sti_dvo *dvo = dev_get_drvdata(dev);
if (dvo->panel)
drm_panel_put(dvo->panel);
drm_bridge_remove(&dvo->bridge);
}

View File

@@ -34,6 +34,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -1982,6 +1983,7 @@ int ltdc_load(struct drm_device *ddev)
if (panel) {
bridge = drmm_panel_bridge_add(ddev, panel);
drm_panel_put(panel);
if (IS_ERR(bridge)) {
drm_err(ddev, "panel-bridge endpoint %d\n", i);
ret = PTR_ERR(bridge);

View File

@@ -1068,20 +1068,20 @@ static int lvds_probe(struct platform_device *pdev)
if (IS_ERR(lvds->base)) {
ret = PTR_ERR(lvds->base);
dev_err(dev, "Unable to get regs %d\n", ret);
return ret;
goto err_put_panel;
}
lvds->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(lvds->pclk)) {
ret = PTR_ERR(lvds->pclk);
dev_err(dev, "Unable to get peripheral clock: %d\n", ret);
return ret;
goto err_put_panel;
}
ret = clk_prepare_enable(lvds->pclk);
if (ret) {
dev_err(dev, "%s: Failed to enable peripheral clk\n", __func__);
return ret;
goto err_put_panel;
}
rstc = devm_reset_control_get_exclusive(dev, NULL);
@@ -1181,6 +1181,9 @@ static int lvds_probe(struct platform_device *pdev)
err_lvds_probe:
clk_disable_unprepare(lvds->pclk);
err_put_panel:
if (lvds->panel)
drm_panel_put(lvds->panel);
return ret;
}
@@ -1189,6 +1192,9 @@ static void lvds_remove(struct platform_device *pdev)
{
struct stm_lvds *lvds = platform_get_drvdata(pdev);
if (lvds->panel)
drm_panel_put(lvds->panel);
lvds_pixel_clk_unregister(lvds);
drm_bridge_remove(&lvds->lvds_bridge);

View File

@@ -18,6 +18,11 @@
#include "sun4i_tcon.h"
#include "sun4i_lvds.h"
static void sun4i_panel_put_action(void *data)
{
drm_panel_put(data);
}
struct sun4i_lvds {
struct drm_connector connector;
struct drm_encoder encoder;
@@ -117,6 +122,14 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
return 0;
}
if (lvds->panel) {
ret = devm_add_action_or_reset(tcon->dev,
sun4i_panel_put_action,
lvds->panel);
if (ret)
return ret;
}
drm_encoder_helper_add(&lvds->encoder,
&sun4i_lvds_enc_helper_funcs);
ret = drm_encoder_init(drm, &lvds->encoder, &sun4i_lvds_enc_funcs,

View File

@@ -43,6 +43,11 @@ drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder)
encoder);
}
static void sun4i_panel_put_action(void *data)
{
drm_panel_put(data);
}
static int sun4i_rgb_get_modes(struct drm_connector *connector)
{
struct sun4i_rgb *rgb =
@@ -209,6 +214,14 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
return 0;
}
if (rgb->panel) {
ret = devm_add_action_or_reset(tcon->dev,
sun4i_panel_put_action,
rgb->panel);
if (ret)
return ret;
}
drm_encoder_helper_add(&rgb->encoder,
&sun4i_rgb_enc_helper_funcs);
ret = drm_encoder_init(drm, &rgb->encoder, &sun4i_rgb_enc_funcs,

View File

@@ -1326,6 +1326,8 @@ static int sun4i_tcon_probe(struct platform_device *pdev)
ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge);
if (ret == -EPROBE_DEFER)
return ret;
if (panel)
drm_panel_put(panel);
}
return component_add(&pdev->dev, &sun4i_tcon_ops);

View File

@@ -971,8 +971,10 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host,
if (IS_ERR(panel))
return PTR_ERR(panel);
if (!dsi->drm || !dsi->drm->registered)
if (!dsi->drm || !dsi->drm->registered) {
drm_panel_put(panel);
return -EPROBE_DEFER;
}
dsi->panel = panel;
dsi->device = device;
@@ -989,6 +991,8 @@ static int sun6i_dsi_detach(struct mipi_dsi_host *host,
{
struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
if (dsi->panel)
drm_panel_put(dsi->panel);
dsi->panel = NULL;
dsi->device = NULL;

View File

@@ -1398,6 +1398,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra194-nvdec", },
{ .compatible = "nvidia,tegra234-vic", },
{ .compatible = "nvidia,tegra234-nvdec", },
{ .compatible = "nvidia,tegra264-vic", },
{ /* sentinel */ }
};

View File

@@ -1527,6 +1527,10 @@ static int tegra_dsi_host_attach(struct mipi_dsi_host *host,
if (!dsi->master) {
struct tegra_output *output = &dsi->output;
/*
* tegra_output_probe() never populates a panel for DSI
* outputs, so output->panel is always NULL here.
*/
output->panel = of_drm_find_panel(device->dev.of_node);
if (IS_ERR(output->panel))
output->panel = NULL;
@@ -1545,6 +1549,7 @@ static int tegra_dsi_host_detach(struct mipi_dsi_host *host,
struct tegra_output *output = &dsi->output;
if (output->panel && &device->dev == output->panel->dev) {
drm_panel_put(output->panel);
output->panel = NULL;
if (output->connector.dev)

View File

@@ -26,8 +26,12 @@ int falcon_wait_idle(struct falcon *falcon)
{
u32 value;
return readl_poll_timeout(falcon->regs + FALCON_IDLESTATE, value,
(value == 0), 10, 100000);
if (falcon->riscv)
return readl_poll_timeout(falcon->regs + RISCV_CPUCTL, value,
(value & RISCV_CPUCTL_ACTIVE_STAT_ACTIVE), 10, 100000);
else
return readl_poll_timeout(falcon->regs + FALCON_IDLESTATE, value,
(value == 0), 10, 100000);
}
static int falcon_dma_wait_not_full(struct falcon *falcon)
@@ -122,6 +126,17 @@ static int falcon_parse_firmware_image(struct falcon *falcon)
return 0;
}
static void falcon_parse_firmware_desc(struct falcon *falcon)
{
struct falcon_fw_riscv_desc *desc =
(struct falcon_fw_riscv_desc *)falcon->firmware.desc_firmware->data;
falcon->firmware.code.offset = desc->code_offset;
falcon->firmware.code.size = desc->code_size;
falcon->firmware.data.offset = desc->data_offset;
falcon->firmware.data.size = desc->data_size;
}
int falcon_read_firmware(struct falcon *falcon, const char *name)
{
int err;
@@ -133,7 +148,23 @@ int falcon_read_firmware(struct falcon *falcon, const char *name)
falcon->firmware.size = falcon->firmware.firmware->size;
if (falcon->riscv) {
/* Load separate descriptor */
char desc_name[128];
scnprintf(desc_name, sizeof(desc_name), "%s.desc", name);
err = request_firmware(&falcon->firmware.desc_firmware, desc_name, falcon->dev);
if (err < 0)
goto release_firmware;
}
return 0;
release_firmware:
release_firmware(falcon->firmware.firmware);
falcon->firmware.firmware = NULL;
return err;
}
int falcon_load_firmware(struct falcon *falcon)
@@ -144,16 +175,22 @@ int falcon_load_firmware(struct falcon *falcon)
/* copy firmware image into local area. this also ensures endianness */
falcon_copy_firmware_image(falcon, firmware);
/* parse the image data */
err = falcon_parse_firmware_image(falcon);
if (err < 0) {
dev_err(falcon->dev, "failed to parse firmware image\n");
return err;
if (falcon->riscv) {
falcon_parse_firmware_desc(falcon);
} else {
err = falcon_parse_firmware_image(falcon);
if (err < 0) {
dev_err(falcon->dev, "failed to parse firmware image\n");
return err;
}
}
release_firmware(firmware);
falcon->firmware.firmware = NULL;
release_firmware(falcon->firmware.desc_firmware);
falcon->firmware.desc_firmware = NULL;
return 0;
}
@@ -168,6 +205,9 @@ void falcon_exit(struct falcon *falcon)
{
if (falcon->firmware.firmware)
release_firmware(falcon->firmware.firmware);
if (falcon->firmware.desc_firmware)
release_firmware(falcon->firmware.desc_firmware);
}
int falcon_boot(struct falcon *falcon)
@@ -229,9 +269,15 @@ int falcon_boot(struct falcon *falcon)
FALCON_ITFEN_CTXEN,
FALCON_ITFEN);
/* boot falcon */
falcon_writel(falcon, 0x00000000, FALCON_BOOTVEC);
falcon_writel(falcon, FALCON_CPUCTL_STARTCPU, FALCON_CPUCTL);
if (falcon->riscv) {
falcon_writel(falcon, RISCV_BCR_CTRL_CORE_SELECT_RISCV, RISCV_BCR_CTRL);
falcon_writel(falcon, 0x0, RISCV_BOOT_VECTOR_HI);
falcon_writel(falcon, 0x100000, RISCV_BOOT_VECTOR_LO);
falcon_writel(falcon, RISCV_CPUCTL_STARTCPU, RISCV_CPUCTL);
} else {
falcon_writel(falcon, 0x00000000, FALCON_BOOTVEC);
falcon_writel(falcon, FALCON_CPUCTL_STARTCPU, FALCON_CPUCTL);
}
err = falcon_wait_idle(falcon);
if (err < 0) {

View File

@@ -55,6 +55,16 @@
#define FALCON_DMATRFFBOFFS 0x0000111c
#define RISCV_BOOT_VECTOR_LO 0x00001780
#define RISCV_BOOT_VECTOR_HI 0x00001784
#define RISCV_CPUCTL 0x00001788
#define RISCV_CPUCTL_STARTCPU (1 << 0)
#define RISCV_CPUCTL_ACTIVE_STAT_ACTIVE (1 << 7)
#define RISCV_BCR_CTRL 0x00001a68
#define RISCV_BCR_CTRL_CORE_SELECT_RISCV (1 << 4)
struct falcon_fw_bin_header_v1 {
u32 magic; /* 0x10de */
u32 version; /* version of bin format (1) */
@@ -76,6 +86,14 @@ struct falcon_fw_os_header_v1 {
u32 data_size;
};
struct falcon_fw_riscv_desc {
u32 reserved[74];
u32 data_offset;
u32 data_size;
u32 code_offset;
u32 code_size;
};
struct falcon_firmware_section {
unsigned long offset;
size_t size;
@@ -84,6 +102,8 @@ struct falcon_firmware_section {
struct falcon_firmware {
/* Firmware after it is read but not loaded */
const struct firmware *firmware;
/* RISC-V firmware descriptor */
const struct firmware *desc_firmware;
/* Raw firmware data */
dma_addr_t iova;
@@ -102,6 +122,9 @@ struct falcon {
struct device *dev;
void __iomem *regs;
/* Peregrine falcon, external boot */
bool riscv;
struct falcon_firmware firmware;
};

View File

@@ -117,11 +117,19 @@ int tegra_output_probe(struct tegra_output *output)
*/
WARN_ON(output->panel || output->bridge);
if (output->panel) {
drm_panel_put(output->panel);
output->panel = NULL;
}
output->panel = of_drm_find_panel(panel);
of_node_put(panel);
if (IS_ERR(output->panel))
return PTR_ERR(output->panel);
if (IS_ERR(output->panel)) {
err = PTR_ERR(output->panel);
output->panel = NULL;
return err;
}
}
ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
@@ -131,7 +139,7 @@ int tegra_output_probe(struct tegra_output *output)
if (!output->ddc) {
err = -EPROBE_DEFER;
return err;
goto put_i2c;
}
}
@@ -185,6 +193,11 @@ int tegra_output_probe(struct tegra_output *output)
return 0;
put_i2c:
if (output->panel) {
drm_panel_put(output->panel);
output->panel = NULL;
}
if (output->ddc)
i2c_put_adapter(output->ddc);
@@ -195,6 +208,11 @@ int tegra_output_probe(struct tegra_output *output)
void tegra_output_remove(struct tegra_output *output)
{
if (output->panel) {
drm_panel_put(output->panel);
output->panel = NULL;
}
if (output->hpd_gpio)
free_irq(output->hpd_irq, output);

View File

@@ -8,6 +8,7 @@
#include <linux/dma-mapping.h>
#include <linux/host1x.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -20,10 +21,16 @@
#include "falcon.h"
#include "vic.h"
#define VIC_FALCON_DEBUGINFO 0x1094
#define VIC_DEBUGINFO_DUMMY 0xabcd1234
#define VIC_DEBUGINFO_CLEAR 0x0
struct vic_config {
const char *firmware;
unsigned int version;
bool supports_sid;
bool has_riscv;
unsigned int transcfg_offset;
};
struct vic {
@@ -54,8 +61,8 @@ static void vic_writel(struct vic *vic, u32 value, unsigned int offset)
static int vic_boot(struct vic *vic)
{
u32 fce_ucode_size, fce_bin_data_offset, stream_id;
void *hdr;
u32 stream_id;
u32 val;
int err = 0;
if (vic->config->supports_sid && tegra_dev_iommu_get_stream_id(vic->dev, &stream_id)) {
@@ -63,7 +70,7 @@ static int vic_boot(struct vic *vic)
value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) |
TRANSCFG_ATT(0, TRANSCFG_SID_HW);
vic_writel(vic, value, VIC_TFBIF_TRANSCFG);
vic_writel(vic, value, vic->config->transcfg_offset);
/*
* STREAMID0 is used for input/output buffers. Initialize it to SID_VIC in case
@@ -85,31 +92,51 @@ static int vic_boot(struct vic *vic)
CG_WAKEUP_DLY_CNT(4),
NV_PVIC_MISC_PRI_VIC_CG);
if (vic->config->has_riscv) {
/* Write a known pattern into DEBUGINFO register */
vic_writel(vic, VIC_DEBUGINFO_DUMMY, VIC_FALCON_DEBUGINFO);
}
err = falcon_boot(&vic->falcon);
if (err < 0)
return err;
hdr = vic->falcon.firmware.virt;
fce_bin_data_offset = *(u32 *)(hdr + VIC_UCODE_FCE_DATA_OFFSET);
if (vic->config->has_riscv) {
/* Check VIC has reached a proper initialized state */
err = readl_poll_timeout(vic->regs + VIC_FALCON_DEBUGINFO, val,
val == VIC_DEBUGINFO_CLEAR,
1000, 2000000);
if (err) {
dev_err(vic->dev, "VIC not initialized, timeout, val=0x%x\n", val);
return err;
}
} else {
u32 fce_ucode_size, fce_bin_data_offset;
dma_addr_t iova;
void *hdr;
/* Old VIC firmware needs kernel help with setting up FCE microcode. */
if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
hdr = vic->falcon.firmware.virt +
*(u32 *)(hdr + VIC_UCODE_FCE_HEADER_OFFSET);
fce_ucode_size = *(u32 *)(hdr + FCE_UCODE_SIZE_OFFSET);
iova = vic->falcon.firmware.iova;
hdr = vic->falcon.firmware.virt;
fce_bin_data_offset = *(u32 *)(hdr + VIC_UCODE_FCE_DATA_OFFSET);
falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_SIZE,
fce_ucode_size);
falcon_execute_method(
&vic->falcon, VIC_SET_FCE_UCODE_OFFSET,
(vic->falcon.firmware.iova + fce_bin_data_offset) >> 8);
}
/* Old VIC firmware needs kernel help with setting up FCE microcode. */
if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
hdr = vic->falcon.firmware.virt +
*(u32 *)(hdr + VIC_UCODE_FCE_HEADER_OFFSET);
fce_ucode_size = *(u32 *)(hdr + FCE_UCODE_SIZE_OFFSET);
err = falcon_wait_idle(&vic->falcon);
if (err < 0) {
dev_err(vic->dev,
"failed to set application ID and FCE base\n");
return err;
falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_SIZE,
fce_ucode_size);
falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_OFFSET,
(iova + fce_bin_data_offset) >> 8);
}
err = falcon_wait_idle(&vic->falcon);
if (err < 0) {
dev_err(vic->dev,
"failed to set application ID and FCE base\n");
return err;
}
}
return 0;
@@ -277,6 +304,8 @@ static int vic_load_firmware(struct vic *vic)
if (!vic->config->supports_sid) {
vic->can_use_context = false;
} else if (vic->config->has_riscv) {
vic->can_use_context = true;
} else if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
/*
* Firmware will access FCE through STREAMID0, so context
@@ -302,7 +331,6 @@ static int vic_load_firmware(struct vic *vic)
return err;
}
static int __maybe_unused vic_runtime_resume(struct device *dev)
{
struct vic *vic = dev_get_drvdata(dev);
@@ -417,6 +445,7 @@ static const struct vic_config vic_t186_config = {
.firmware = NVIDIA_TEGRA_186_VIC_FIRMWARE,
.version = 0x18,
.supports_sid = true,
.transcfg_offset = 0x2044,
};
#define NVIDIA_TEGRA_194_VIC_FIRMWARE "nvidia/tegra194/vic.bin"
@@ -425,6 +454,7 @@ static const struct vic_config vic_t194_config = {
.firmware = NVIDIA_TEGRA_194_VIC_FIRMWARE,
.version = 0x19,
.supports_sid = true,
.transcfg_offset = 0x2044,
};
#define NVIDIA_TEGRA_234_VIC_FIRMWARE "nvidia/tegra234/vic.bin"
@@ -433,6 +463,18 @@ static const struct vic_config vic_t234_config = {
.firmware = NVIDIA_TEGRA_234_VIC_FIRMWARE,
.version = 0x23,
.supports_sid = true,
.transcfg_offset = 0x2044,
};
#define NVIDIA_TEGRA_264_VIC_FIRMWARE "nvidia/tegra264/vic.bin"
#define NVIDIA_TEGRA_264_VIC_DESC "nvidia/tegra264/vic.bin.desc"
static const struct vic_config vic_t264_config = {
.firmware = NVIDIA_TEGRA_264_VIC_FIRMWARE,
.version = 0x264,
.supports_sid = true,
.has_riscv = true,
.transcfg_offset = 0x2244,
};
static const struct of_device_id tegra_vic_of_match[] = {
@@ -441,6 +483,7 @@ static const struct of_device_id tegra_vic_of_match[] = {
{ .compatible = "nvidia,tegra186-vic", .data = &vic_t186_config },
{ .compatible = "nvidia,tegra194-vic", .data = &vic_t194_config },
{ .compatible = "nvidia,tegra234-vic", .data = &vic_t234_config },
{ .compatible = "nvidia,tegra264-vic", .data = &vic_t264_config },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_vic_of_match);
@@ -495,6 +538,7 @@ static int vic_probe(struct platform_device *pdev)
vic->falcon.dev = dev;
vic->falcon.regs = vic->regs;
vic->falcon.riscv = vic->config->has_riscv;
err = falcon_init(&vic->falcon);
if (err < 0)
@@ -571,3 +615,7 @@ MODULE_FIRMWARE(NVIDIA_TEGRA_194_VIC_FIRMWARE);
#if IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
MODULE_FIRMWARE(NVIDIA_TEGRA_234_VIC_FIRMWARE);
#endif
#if IS_ENABLED(CONFIG_ARCH_TEGRA_264_SOC)
MODULE_FIRMWARE(NVIDIA_TEGRA_264_VIC_FIRMWARE);
MODULE_FIRMWARE(NVIDIA_TEGRA_264_VIC_DESC);
#endif

View File

@@ -21,11 +21,10 @@
#define CG_IDLE_CG_EN (1 << 6)
#define CG_WAKEUP_DLY_CNT(val) ((val & 0xf) << 16)
#define VIC_TFBIF_TRANSCFG 0x00002044
#define TRANSCFG_ATT(i, v) (((v) & 0x3) << (i * 4))
#define TRANSCFG_SID_HW 0
#define TRANSCFG_SID_PHY 1
#define TRANSCFG_SID_FALCON 2
#define TRANSCFG_ATT(i, v) (((v) & 0x3) << ((i) * 4))
#define TRANSCFG_SID_HW 0
#define TRANSCFG_SID_PHY 1
#define TRANSCFG_SID_FALCON 2
/* Firmware offsets */

View File

@@ -162,6 +162,7 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss)
if (panel) {
u32 conn_type;
int ret;
dev_dbg(dev, "Setting up panel for port %d\n", i);
@@ -176,7 +177,8 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss)
break;
default:
WARN_ON(1);
return -EINVAL;
ret = -EINVAL;
goto put_panel;
}
if (panel->connector_type != conn_type) {
@@ -184,16 +186,20 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss)
"%s: Panel %s has incompatible connector type for vp%d (%d != %d)\n",
__func__, dev_name(panel->dev), i,
panel->connector_type, conn_type);
return -EINVAL;
ret = -EINVAL;
goto put_panel;
}
bridge = devm_drm_panel_bridge_add(dev, panel);
if (IS_ERR(bridge)) {
ret = PTR_ERR_OR_ZERO(bridge);
if (ret)
dev_err(dev,
"failed to set up panel bridge for port %d\n",
i);
return PTR_ERR(bridge);
}
put_panel:
drm_panel_put(panel);
if (ret)
return ret;
}
pipes[num_pipes].hw_videoport = i;

View File

@@ -84,6 +84,7 @@ static int tve200_modeset_init(struct drm_device *dev)
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_Unknown);
drm_panel_put(panel);
if (IS_ERR(bridge)) {
ret = PTR_ERR(bridge);
goto out_bridge;
@@ -263,6 +264,7 @@ static const struct of_device_id tve200_of_match[] = {
},
{},
};
MODULE_DEVICE_TABLE(of, tve200_of_match);
static struct platform_driver tve200_driver = {
.driver = {

View File

@@ -375,6 +375,11 @@ struct v3d_job {
void (*free)(struct kref *ref);
bool has_pm_ref;
/* Whether the job needs implicit dependencies, i.e. must wait for
* other contexts still writing its BOs.
*/
bool has_implicit_dep;
};
struct v3d_bin_job {

View File

@@ -46,6 +46,9 @@ v3d_submit_lock_reservations(struct v3d_submit *submit)
for (i = 0; i < submit->job_count; i++) {
struct v3d_job *job = submit->jobs[i];
if (!job->has_implicit_dep)
continue;
for (j = 0; j < job->bo_count; j++) {
ret = drm_sched_job_add_implicit_dependencies(&job->base,
job->bo[j],
@@ -71,7 +74,6 @@ v3d_submit_unlock_reservations(struct v3d_submit *submit)
/**
* v3d_lookup_bos() - Sets up job->bo[] with the GEM objects
* referenced by the job.
* @dev: DRM device
* @file_priv: DRM file for this fd
* @job: V3D job being set up
* @bo_handles: GEM handles
@@ -85,23 +87,44 @@ v3d_submit_unlock_reservations(struct v3d_submit *submit)
* failure, because that will happen at `v3d_job_free()`.
*/
static int
v3d_lookup_bos(struct v3d_submit *submit, u64 bo_handles, u32 bo_count)
v3d_lookup_bos(struct drm_file *file_priv, struct v3d_job *job,
u64 bo_handles, u32 bo_count)
{
struct v3d_job *last_job = submit->jobs[submit->job_count - 1];
last_job->bo_count = bo_count;
if (!last_job->bo_count) {
/* See comment on bo_index for why we have to check
* this.
*/
drm_warn(&submit->v3d->drm, "Rendering requires BOs\n");
if (!bo_count) {
drm_warn(&job->v3d->drm, "Rendering requires BOs\n");
return -EINVAL;
}
return drm_gem_objects_lookup(submit->file_priv,
job->bo_count = bo_count;
return drm_gem_objects_lookup(file_priv,
(void __user *)(uintptr_t)bo_handles,
last_job->bo_count, &last_job->bo);
job->bo_count, &job->bo);
}
/**
* v3d_job_reference_bos() - Share another job's BOs with @dst
* @dst: job that acquires references to the BOs
* @src: job whose already-resolved BO list is shared
*
* For submissions with multiple jobs that use the same BOs, a trailing job
* shouldn't look the handles up again, as it could cause inconsistencies.
* Instead, it should reference the previous job's BOs.
*/
static int
v3d_job_reference_bos(struct v3d_job *dst, struct v3d_job *src)
{
dst->bo = kvmalloc_objs(*dst->bo, src->bo_count);
if (!dst->bo)
return -ENOMEM;
dst->bo_count = src->bo_count;
for (int i = 0; i < dst->bo_count; i++) {
dst->bo[i] = src->bo[i];
drm_gem_object_get(dst->bo[i]);
}
return 0;
}
static void
@@ -223,13 +246,14 @@ v3d_job_add_syncobjs(struct v3d_job *job, struct drm_file *file_priv,
static const struct {
size_t size;
void (*free)(struct kref *ref);
bool has_implicit_dep;
} v3d_job_types[] = {
[V3D_BIN] = { sizeof(struct v3d_bin_job), v3d_job_free },
[V3D_RENDER] = { sizeof(struct v3d_render_job), v3d_render_job_free },
[V3D_TFU] = { sizeof(struct v3d_tfu_job), v3d_job_free },
[V3D_CSD] = { sizeof(struct v3d_csd_job), v3d_job_free },
[V3D_CACHE_CLEAN] = { sizeof(struct v3d_job), v3d_job_free },
[V3D_CPU] = { sizeof(struct v3d_cpu_job), v3d_cpu_job_free },
[V3D_BIN] = { sizeof(struct v3d_bin_job), v3d_job_free, false },
[V3D_RENDER] = { sizeof(struct v3d_render_job), v3d_render_job_free, true },
[V3D_TFU] = { sizeof(struct v3d_tfu_job), v3d_job_free, true },
[V3D_CSD] = { sizeof(struct v3d_csd_job), v3d_job_free, true },
[V3D_CACHE_CLEAN] = { sizeof(struct v3d_job), v3d_job_free, false },
[V3D_CPU] = { sizeof(struct v3d_cpu_job), v3d_cpu_job_free, true },
};
static struct v3d_job *
@@ -251,6 +275,7 @@ v3d_submit_add_job(struct v3d_submit *submit, enum v3d_queue queue)
job->queue = queue;
job->file_priv = v3d_priv;
job->free = v3d_job_types[queue].free;
job->has_implicit_dep = v3d_job_types[queue].has_implicit_dep;
ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue],
1, v3d_priv, submit->file_priv->client_id);
@@ -518,13 +543,18 @@ v3d_setup_csd_jobs_and_bos(struct v3d_submit *submit,
if (ret)
return ret;
ret = v3d_lookup_bos(submit->file_priv, &job->base, args->bo_handles,
args->bo_handle_count);
if (ret)
return ret;
job->args = *args;
clean_job = v3d_submit_add_job(submit, V3D_CACHE_CLEAN);
if (IS_ERR(clean_job))
return PTR_ERR(clean_job);
return v3d_lookup_bos(submit, args->bo_handles, args->bo_handle_count);
return v3d_job_reference_bos(clean_job, &job->base);
}
static void
@@ -1163,22 +1193,33 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
if (ret)
goto fail;
/*
* We don't associate the BOs with the BIN job. Fences are only
* attached to the last job in the submission chain, and BIN jobs
* don't need implicit dependencies since depending on results from
* another context is not a realistic scenario for binning.
*/
ret = v3d_lookup_bos(submit.file_priv, &render->base,
args->bo_handles, args->bo_handle_count);
if (ret)
goto fail;
if (args->flags & DRM_V3D_SUBMIT_CL_FLUSH_CACHE) {
clean_job = v3d_submit_add_job(&submit, V3D_CACHE_CLEAN);
if (IS_ERR(clean_job)) {
ret = PTR_ERR(clean_job);
goto fail;
}
ret = v3d_job_reference_bos(clean_job, &render->base);
if (ret)
goto fail;
}
ret = v3d_attach_perfmon_to_jobs(&submit, args->perfmon_id);
if (ret)
goto fail;
ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count);
if (ret)
goto fail;
ret = v3d_submit_lock_reservations(&submit);
if (ret)
goto fail;
@@ -1557,7 +1598,8 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data,
* the CSD and clean jobs in the case of indirect CSD job.
*/
if (args->bo_handle_count) {
ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count);
ret = v3d_lookup_bos(submit.file_priv, &cpu_job->base,
args->bo_handles, args->bo_handle_count);
if (ret)
goto fail;

View File

@@ -3208,11 +3208,11 @@ static int vc4_hdmi_runtime_resume(struct device *dev)
return ret;
}
static void vc4_hdmi_put_ddc_device(void *ptr)
static void vc4_hdmi_put_ddc(void *ptr)
{
struct vc4_hdmi *vc4_hdmi = ptr;
put_device(&vc4_hdmi->ddc->dev);
i2c_put_adapter(vc4_hdmi->ddc);
}
static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
@@ -3266,14 +3266,14 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
return -ENODEV;
}
vc4_hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
vc4_hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!vc4_hdmi->ddc) {
drm_err(drm, "Failed to get ddc i2c adapter by node\n");
return -EPROBE_DEFER;
}
ret = devm_add_action_or_reset(dev, vc4_hdmi_put_ddc_device, vc4_hdmi);
ret = devm_add_action_or_reset(dev, vc4_hdmi_put_ddc, vc4_hdmi);
if (ret)
return ret;

View File

@@ -17,7 +17,8 @@ host1x-y = \
hw/host1x05.o \
hw/host1x06.o \
hw/host1x07.o \
hw/host1x08.o
hw/host1x08.o \
hw/host1x10.o
host1x-$(CONFIG_IOMMU_API) += \
context.o

View File

@@ -508,7 +508,7 @@ static int host1x_device_add(struct host1x *host1x,
* Add device even if there are no subdevs to ensure syncpoint functionality
* is available regardless of whether any engine subdevices are present
*/
if (list_empty(&device->subdevs)) {
if (list_empty(&device->subdevs) && !device->registered) {
err = device_add(&device->dev);
if (err < 0)
dev_err(&device->dev, "failed to add device: %d\n", err);

View File

@@ -13,6 +13,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/overflow.h>
#include <linux/slab.h>
#include <trace/events/host1x.h>
@@ -419,7 +420,7 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
/* won't need a timeout when replayed */
job->timeout = 0;
syncpt_incrs = job->syncpt_end - syncpt_val;
syncpt_incrs = wrapping_sub(u32, job->syncpt_end, syncpt_val);
dev_dbg(dev, "%s: CPU incr (%d)\n", __func__, syncpt_incrs);
host1x_job_dump(dev, job);

View File

@@ -24,7 +24,7 @@ int host1x_memory_context_list_init(struct host1x *host1x)
struct host1x_memory_context_list *cdl = &host1x->context_list;
struct device_node *node = host1x->dev->of_node;
struct host1x_memory_context *ctx;
unsigned int i;
unsigned int devs, i;
int err;
cdl->devs = NULL;
@@ -35,7 +35,16 @@ int host1x_memory_context_list_init(struct host1x *host1x)
if (err < 0)
return 0;
cdl->len = err / 4;
devs = 0;
for (i = 0; i < err / 4; i++) {
u32 length;
of_property_read_u32_index(node, "iommu-map", i * 4 + 3, &length);
devs += length;
}
cdl->len = devs;
cdl->devs = kzalloc_objs(*cdl->devs, cdl->len);
if (!cdl->devs)
return -ENOMEM;

View File

@@ -31,7 +31,7 @@ void host1x_debug_output(struct output *o, const char *fmt, ...)
int len;
va_start(args, fmt);
len = vsnprintf(o->buf, sizeof(o->buf), fmt, args);
len = vscnprintf(o->buf, sizeof(o->buf), fmt, args);
va_end(args);
o->fn(o->ctx, o->buf, len, false);
@@ -43,7 +43,7 @@ void host1x_debug_cont(struct output *o, const char *fmt, ...)
int len;
va_start(args, fmt);
len = vsnprintf(o->buf, sizeof(o->buf), fmt, args);
len = vscnprintf(o->buf, sizeof(o->buf), fmt, args);
va_end(args);
o->fn(o->ctx, o->buf, len, true);

View File

@@ -41,6 +41,7 @@
#include "hw/host1x06.h"
#include "hw/host1x07.h"
#include "hw/host1x08.h"
#include "hw/host1x10.h"
void host1x_common_writel(struct host1x *host1x, u32 v, u32 r)
{
@@ -287,7 +288,47 @@ static const struct host1x_info host1x08_info = {
.reserve_vblank_syncpts = false,
};
static const struct host1x_sid_entry tegra264_sid_table[] = {
{ /* SE1 MMIO */ .base = 0x1650, .offset = 0x90, .limit = 0x90 },
{ /* SE2 MMIO */ .base = 0x1658, .offset = 0x90, .limit = 0x90 },
{ /* SE4 MMIO */ .base = 0x1660, .offset = 0x90, .limit = 0x90 },
{ /* SE1 ch */ .base = 0x1738, .offset = 0x90, .limit = 0x90 },
{ /* SE2 ch */ .base = 0x1740, .offset = 0x90, .limit = 0x90 },
{ /* SE4 ch */ .base = 0x1748, .offset = 0x90, .limit = 0x90 },
{ /* VIC ch */ .base = 0x1790, .offset = 0x30, .limit = 0x30 },
{ /* VIC MMIO */ .base = 0x1688, .offset = 0x34, .limit = 0x34 },
{ /* TSEC MMIO */ .base = 0x1690, .offset = 0x30, .limit = 0x34 },
{ /* VI MMIO */ .base = 0x1698, .offset = 0x800, .limit = 0x800 },
{ /* VI_THI MMIO */ .base = 0x16a0, .offset = 0x30, .limit = 0x34 },
{ /* ISP MMIO */ .base = 0x1680, .offset = 0x800, .limit = 0x800 },
{ /* ISP_THI MMIO */ .base = 0x16a8, .offset = 0x30, .limit = 0x34 },
{ /* VI2 MMIO */ .base = 0x16b8, .offset = 0x800, .limit = 0x800 },
{ /* VI2_THI MMIO */ .base = 0x16c0, .offset = 0x30, .limit = 0x34 },
{ /* ISP1 MMIO */ .base = 0x16c8, .offset = 0x800, .limit = 0x800 },
{ /* ISP1_THI MMIO */ .base = 0x16d0, .offset = 0x30, .limit = 0x34 },
};
static const struct host1x_info host1x10_info = {
.nb_channels = 63,
.nb_pts = 1024,
.nb_mlocks = 24,
.nb_bases = 0,
.init = host1x10_init,
.sync_offset = 0x0,
.dma_mask = DMA_BIT_MASK(40),
.has_wide_gather = true,
.has_hypervisor = true,
.has_common = true,
.num_sid_entries = ARRAY_SIZE(tegra264_sid_table),
.sid_table = tegra264_sid_table,
.streamid_vm_table = { 0x1004, 128 },
.classid_vm_table = { 0x1404, 25 },
.mmio_vm_table = { 0x1504, 25 },
.reserve_vblank_syncpts = false,
};
static const struct of_device_id host1x_of_match[] = {
{ .compatible = "nvidia,tegra264-host1x", .data = &host1x10_info, },
{ .compatible = "nvidia,tegra234-host1x", .data = &host1x08_info, },
{ .compatible = "nvidia,tegra194-host1x", .data = &host1x07_info, },
{ .compatible = "nvidia,tegra186-host1x", .data = &host1x06_info, },

View File

@@ -246,23 +246,24 @@ static void timeout_release_mlock(struct host1x_cdma *cdma)
* so it turns out that if we don't /actually/ need MLOCKs, we can just
* ignore them.
*
* As such, for now just implement this on Tegra234 where things are
* stricter but also easy to implement.
* As such, for now just implement this on Tegra234 and above where things
* are stricter but also easy to implement.
*/
struct host1x_channel *ch = cdma_to_channel(cdma);
struct host1x *host1x = cdma_to_host1x(cdma);
u32 offset;
switch (ch->client->class) {
case HOST1X_CLASS_VIC:
offset = HOST1X_COMMON_VIC_MLOCK;
break;
#if HOST1X_HW == 8
case HOST1X_CLASS_NVJPG1:
offset = HOST1X_COMMON_NVJPG1_MLOCK;
break;
case HOST1X_CLASS_NVENC:
offset = HOST1X_COMMON_NVENC_MLOCK;
break;
case HOST1X_CLASS_VIC:
offset = HOST1X_COMMON_VIC_MLOCK;
break;
case HOST1X_CLASS_NVJPG:
offset = HOST1X_COMMON_NVJPG_MLOCK;
break;
@@ -272,6 +273,7 @@ static void timeout_release_mlock(struct host1x_cdma *cdma)
case HOST1X_CLASS_OFA:
offset = HOST1X_COMMON_OFA_MLOCK;
break;
#endif
default:
WARN(1, "%s was not updated for class %u", __func__, ch->client->class);
return;
@@ -355,7 +357,7 @@ static int cdma_timeout_init(struct host1x_cdma *cdma)
static void cdma_timeout_destroy(struct host1x_cdma *cdma)
{
if (cdma->timeout.initialized)
cancel_delayed_work(&cdma->timeout.wq);
cancel_delayed_work_sync(&cdma->timeout.wq);
cdma->timeout.initialized = false;
}

View File

@@ -7,6 +7,7 @@
#include <linux/host1x.h>
#include <linux/iommu.h>
#include <linux/overflow.h>
#include <linux/slab.h>
#include <trace/events/host1x.h>
@@ -36,10 +37,9 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo,
for (i = 0; i < words; i += TRACE_MAX_LENGTH) {
u32 num_words = min(words - i, TRACE_MAX_LENGTH);
offset += i * sizeof(u32);
trace_host1x_cdma_push_gather(dev_name(dev), bo,
num_words, offset,
num_words,
offset + i * sizeof(u32),
mem);
}
@@ -121,7 +121,8 @@ static void submit_gathers(struct host1x_job *job, struct host1x_job_cmd *cmds,
if (cmd->is_wait) {
if (cmd->wait.relative)
threshold = job_syncpt_base + cmd->wait.threshold;
threshold = wrapping_add(u32, job_syncpt_base,
cmd->wait.threshold);
else
threshold = cmd->wait.threshold;
@@ -260,7 +261,8 @@ static void channel_program_cdma(struct host1x_job *job)
/* Submit work. */
job->syncpt_end = host1x_syncpt_incr_max(sp, job->syncpt_incrs);
submit_gathers(job, job->cmds + i, job->num_cmds - i, job->syncpt_end - job->syncpt_incrs);
submit_gathers(job, job->cmds + i, job->num_cmds - i,
wrapping_sub(u32, job->syncpt_end, job->syncpt_incrs));
/* Before releasing MLOCK, ensure engine is idle again. */
fence = host1x_syncpt_incr_max(sp, 1);
@@ -298,7 +300,8 @@ static void channel_program_cdma(struct host1x_job *job)
job->syncpt_end = host1x_syncpt_incr_max(sp, job->syncpt_incrs);
submit_gathers(job, job->cmds, job->num_cmds, job->syncpt_end - job->syncpt_incrs);
submit_gathers(job, job->cmds, job->num_cmds,
wrapping_sub(u32, job->syncpt_end, job->syncpt_incrs));
#endif
}

View File

@@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Host1x init for Tegra264 SoCs
*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
/* include hw specification */
#include "host1x10.h"
#include "host1x10_hardware.h"
/* include code */
#define HOST1X_HW 10
#include "cdma_hw.c"
#include "channel_hw.c"
#include "debug_hw.c"
#include "intr_hw.c"
#include "syncpt_hw.c"
#include "../dev.h"
int host1x10_init(struct host1x *host)
{
host->channel_op = &host1x_channel_ops;
host->cdma_op = &host1x_cdma_ops;
host->cdma_pb_op = &host1x_pushbuffer_ops;
host->syncpt_op = &host1x_syncpt_ops;
host->intr_op = &host1x_intr_ops;
host->debug_op = &host1x_debug_ops;
return 0;
}

View File

@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Host1x init for Tegra264 SoCs
*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#ifndef HOST1X_HOST1X10_H
#define HOST1X_HOST1X10_H
struct host1x;
int host1x10_init(struct host1x *host);
#endif

View File

@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Tegra host1x Register Offsets for Tegra264
*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#ifndef __HOST1X_HOST1X10_HARDWARE_H
#define __HOST1X_HOST1X10_HARDWARE_H
#include <linux/types.h>
#include <linux/bitops.h>
#include "hw_host1x10_uclass.h"
#include "hw_host1x10_vm.h"
#include "hw_host1x10_hypervisor.h"
#include "hw_host1x10_common.h"
#include "opcodes.h"
#endif

View File

@@ -0,0 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#define HOST1X_COMMON_VIC_MLOCK 0x4060

View File

@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#define HOST1X_HV_SYNCPT_PROT_EN 0x172c
#define HOST1X_HV_SYNCPT_PROT_EN_CH_EN BIT(1)
#define HOST1X_HV_CH_MLOCK_EN(x) (0x1708 + (x * 4))
#define HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(x) (0x1718 + (x * 4))
#define HOST1X_HV_SYNCPT_VM(x) (0x0 + 4 * (x))

View File

@@ -0,0 +1,181 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
/*
* Function naming determines intended use:
*
* <x>_r(void) : Returns the offset for register <x>.
*
* <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
*
* <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
*
* <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
* and masked to place it at field <y> of register <x>. This value
* can be |'d with others to produce a full register value for
* register <x>.
*
* <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This
* value can be ~'d and then &'d to clear the value of field <y> for
* register <x>.
*
* <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
* to place it at field <y> of register <x>. This value can be |'d
* with others to produce a full register value for <x>.
*
* <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
* <x> value 'r' after being shifted to place its LSB at bit 0.
* This value is suitable for direct comparison with other unshifted
* values appropriate for use in field <y> of register <x>.
*
* <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
* field <y> of register <x>. This value is suitable for direct
* comparison with unshifted values appropriate for use in field <y>
* of register <x>.
*/
#ifndef HOST1X_HW_HOST1X10_UCLASS_H
#define HOST1X_HW_HOST1X10_UCLASS_H
static inline u32 host1x_uclass_incr_syncpt_r(void)
{
return 0x0;
}
#define HOST1X_UCLASS_INCR_SYNCPT \
host1x_uclass_incr_syncpt_r()
static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
{
return (v & 0xff) << 10;
}
#define HOST1X_UCLASS_INCR_SYNCPT_COND_F(v) \
host1x_uclass_incr_syncpt_cond_f(v)
static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
{
return (v & 0x3ff) << 0;
}
#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
host1x_uclass_incr_syncpt_indx_f(v)
static inline u32 host1x_uclass_wait_syncpt_r(void)
{
return 0x8;
}
#define HOST1X_UCLASS_WAIT_SYNCPT \
host1x_uclass_wait_syncpt_r()
static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v)
{
return (v & 0xff) << 24;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_INDX_F(v) \
host1x_uclass_wait_syncpt_indx_f(v)
static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v)
{
return (v & 0xffffff) << 0;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_THRESH_F(v) \
host1x_uclass_wait_syncpt_thresh_f(v)
static inline u32 host1x_uclass_wait_syncpt_base_r(void)
{
return 0x9;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE \
host1x_uclass_wait_syncpt_base_r()
static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v)
{
return (v & 0xff) << 24;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_INDX_F(v) \
host1x_uclass_wait_syncpt_base_indx_f(v)
static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v)
{
return (v & 0xff) << 16;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_BASE_INDX_F(v) \
host1x_uclass_wait_syncpt_base_base_indx_f(v)
static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v)
{
return (v & 0xffff) << 0;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \
host1x_uclass_wait_syncpt_base_offset_f(v)
static inline u32 host1x_uclass_load_syncpt_base_r(void)
{
return 0xb;
}
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE \
host1x_uclass_load_syncpt_base_r()
static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v)
{
return (v & 0xff) << 24;
}
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(v) \
host1x_uclass_load_syncpt_base_base_indx_f(v)
static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v)
{
return (v & 0xffffff) << 0;
}
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(v) \
host1x_uclass_load_syncpt_base_value_f(v)
static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v)
{
return (v & 0xff) << 24;
}
#define HOST1X_UCLASS_INCR_SYNCPT_BASE_BASE_INDX_F(v) \
host1x_uclass_incr_syncpt_base_base_indx_f(v)
static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v)
{
return (v & 0xffffff) << 0;
}
#define HOST1X_UCLASS_INCR_SYNCPT_BASE_OFFSET_F(v) \
host1x_uclass_incr_syncpt_base_offset_f(v)
static inline u32 host1x_uclass_indoff_r(void)
{
return 0x2d;
}
#define HOST1X_UCLASS_INDOFF \
host1x_uclass_indoff_r()
static inline u32 host1x_uclass_indoff_indbe_f(u32 v)
{
return (v & 0xf) << 28;
}
#define HOST1X_UCLASS_INDOFF_INDBE_F(v) \
host1x_uclass_indoff_indbe_f(v)
static inline u32 host1x_uclass_indoff_autoinc_f(u32 v)
{
return (v & 0x1) << 27;
}
#define HOST1X_UCLASS_INDOFF_AUTOINC_F(v) \
host1x_uclass_indoff_autoinc_f(v)
static inline u32 host1x_uclass_indoff_indmodid_f(u32 v)
{
return (v & 0xff) << 18;
}
#define HOST1X_UCLASS_INDOFF_INDMODID_F(v) \
host1x_uclass_indoff_indmodid_f(v)
static inline u32 host1x_uclass_indoff_indroffset_f(u32 v)
{
return (v & 0xffff) << 2;
}
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
host1x_uclass_indoff_indroffset_f(v)
static inline u32 host1x_uclass_indoff_rwn_read_v(void)
{
return 1;
}
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
host1x_uclass_indoff_indroffset_f(v)
static inline u32 host1x_uclass_load_syncpt_payload_32_r(void)
{
return 0x4e;
}
#define HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32 \
host1x_uclass_load_syncpt_payload_32_r()
static inline u32 host1x_uclass_wait_syncpt_32_r(void)
{
return 0x50;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_32 \
host1x_uclass_wait_syncpt_32_r()
#endif

View File

@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#define HOST1X_CHANNEL_DMASTART 0x0000
#define HOST1X_CHANNEL_DMASTART_HI 0x0004
#define HOST1X_CHANNEL_DMAPUT 0x0008
#define HOST1X_CHANNEL_DMAPUT_HI 0x000c
#define HOST1X_CHANNEL_DMAGET 0x0010
#define HOST1X_CHANNEL_DMAGET_HI 0x0014
#define HOST1X_CHANNEL_DMAEND 0x0018
#define HOST1X_CHANNEL_DMAEND_HI 0x001c
#define HOST1X_CHANNEL_DMACTRL 0x0020
#define HOST1X_CHANNEL_DMACTRL_DMASTOP BIT(0)
#define HOST1X_CHANNEL_DMACTRL_DMAGETRST BIT(1)
#define HOST1X_CHANNEL_DMACTRL_DMAINITGET BIT(2)
#define HOST1X_CHANNEL_CMDFIFO_STAT 0x0024
#define HOST1X_CHANNEL_CMDFIFO_STAT_EMPTY BIT(13)
#define HOST1X_CHANNEL_CMDFIFO_RDATA 0x0028
#define HOST1X_CHANNEL_CMDP_OFFSET 0x0030
#define HOST1X_CHANNEL_CMDP_CLASS 0x0034
#define HOST1X_CHANNEL_CHANNELSTAT 0x0038
#define HOST1X_CHANNEL_CMDPROC_STOP 0x0048
#define HOST1X_CHANNEL_TEARDOWN 0x004c
#define HOST1X_CHANNEL_SMMU_STREAMID 0x0084
#define HOST1X_SYNC_SYNCPT_CPU_INCR(x) (0x6400 + 4 * (x))
#define HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(x) (0x6600 + 4 * (x))
#define HOST1X_SYNC_SYNCPT_INTR_DEST(x) (0x6684 + 4 * (x))
#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(x) (0x770c + 4 * (x))
#define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(x) (0x7790 + 4 * (x))
#define HOST1X_SYNC_SYNCPT(x) (0x8080 + 4 * (x))
#define HOST1X_SYNC_SYNCPT_INT_THRESH(x) (0xa088 + 4 * (x))
#define HOST1X_SYNC_SYNCPT_CH_APP(x) (0xb090 + 4 * (x))
#define HOST1X_SYNC_SYNCPT_CH_APP_CH(v) (((v) & 0x3f) << 8)

View File

@@ -7,6 +7,7 @@
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/overflow.h>
#include "dev.h"
#include "fence.h"
#include "intr.h"
@@ -17,7 +18,7 @@ static void host1x_intr_add_fence_to_list(struct host1x_fence_list *list,
struct host1x_syncpt_fence *fence_in_list;
list_for_each_entry_reverse(fence_in_list, &list->list, list) {
if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) {
if ((s32)wrapping_sub(u32, fence_in_list->threshold, fence->threshold) <= 0) {
/* Fence in list is before us, we can insert here */
list_add(&fence->list, &fence_in_list->list);
return;
@@ -83,7 +84,7 @@ void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id)
spin_lock(&sp->fences.lock);
list_for_each_entry_safe(fence, tmp, &sp->fences.list, list) {
if (((value - fence->threshold) & 0x80000000U) != 0U) {
if ((wrapping_sub(u32, value, fence->threshold) & 0x80000000U) != 0U) {
/* Fence is not yet expired, we are done */
break;
}

View File

@@ -138,7 +138,7 @@ void host1x_job_add_wait(struct host1x_job *job, u32 id, u32 thresh,
}
EXPORT_SYMBOL(host1x_job_add_wait);
static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
static int pin_job(struct host1x *host, struct host1x_job *job)
{
unsigned long mask = HOST1X_RELOC_READ | HOST1X_RELOC_WRITE;
struct host1x_client *client = job->client;

Some files were not shown because too many files have changed in this diff Show More