mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 12:13:51 -04:00
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:
3
.mailmap
3
.mailmap
@@ -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>
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -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
|
||||
|
||||
@@ -22,6 +22,7 @@ properties:
|
||||
- nvidia,tegra186-vic
|
||||
- nvidia,tegra194-vic
|
||||
- nvidia,tegra234-vic
|
||||
- nvidia,tegra264-vic
|
||||
|
||||
- items:
|
||||
- const: nvidia,tegra132-vic
|
||||
|
||||
@@ -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:
|
||||
- |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
722
drivers/gpu/drm/drm_panel.c.orig
Normal file
722
drivers/gpu/drm/drm_panel.c.orig
Normal 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");
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
488
drivers/gpu/drm/panel/panel-novatek-nt36536.c
Normal file
488
drivers/gpu/drm/panel/panel-novatek-nt36536.c
Normal 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");
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 */ }
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
33
drivers/gpu/host1x/hw/host1x10.c
Normal file
33
drivers/gpu/host1x/hw/host1x10.c
Normal 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;
|
||||
}
|
||||
15
drivers/gpu/host1x/hw/host1x10.h
Normal file
15
drivers/gpu/host1x/hw/host1x10.h
Normal 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
|
||||
21
drivers/gpu/host1x/hw/host1x10_hardware.h
Normal file
21
drivers/gpu/host1x/hw/host1x10_hardware.h
Normal 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
|
||||
6
drivers/gpu/host1x/hw/hw_host1x10_common.h
Normal file
6
drivers/gpu/host1x/hw/hw_host1x10_common.h
Normal 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
|
||||
10
drivers/gpu/host1x/hw/hw_host1x10_hypervisor.h
Normal file
10
drivers/gpu/host1x/hw/hw_host1x10_hypervisor.h
Normal 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))
|
||||
181
drivers/gpu/host1x/hw/hw_host1x10_uclass.h
Normal file
181
drivers/gpu/host1x/hw/hw_host1x10_uclass.h
Normal 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
|
||||
36
drivers/gpu/host1x/hw/hw_host1x10_vm.h
Normal file
36
drivers/gpu/host1x/hw/hw_host1x10_vm.h
Normal 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)
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user