Commit 8daecf61 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm/tegra/for-5.20-rc1' of https://gitlab.freedesktop.org/drm/tegra into drm-next

drm/tegra: Changes for v5.20-rc1

The bulk of these changes adds support for context isolation for the
various supported host1x engines, as well as support for the hardware
found on the new Tegra234 SoC generation.

There's also a couple of fixes and cleanups. To round things off, the
device tree bindings are converted to the new json-schema format that
allows DTBs to be validated.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Thierry Reding <thierry.reding@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220708181136.673789-1-thierry.reding@gmail.com
parents b45b4f88 135f4c55
NVIDIA Tegra MIPI pad calibration controller
Required properties:
- compatible: "nvidia,tegra<chip>-mipi"
- reg: Physical base address and length of the controller's registers.
- clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
- mipi-cal
- #nvidia,mipi-calibrate-cells: Should be 1. The cell is a bitmask of the pads
that need to be calibrated for a given device.
User nodes need to contain an nvidia,mipi-calibrate property that has a
phandle to refer to the calibration controller node and a bitmask of the pads
that need to be calibrated.
Example:
mipi: mipi@700e3000 {
compatible = "nvidia,tegra114-mipi";
reg = <0x700e3000 0x100>;
clocks = <&tegra_car TEGRA114_CLK_MIPI_CAL>;
clock-names = "mipi-cal";
#nvidia,mipi-calibrate-cells = <1>;
};
...
host1x@50000000 {
...
dsi@54300000 {
...
nvidia,mipi-calibrate = <&mipi 0x060>;
...
};
...
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra114-mipi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra MIPI pad calibration controller
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^mipi@[0-9a-f]+$"
compatible:
enum:
- nvidia,tegra114-mipi
- nvidia,tegra210-mipi
- nvidia,tegra186-mipi
reg:
maxItems: 1
clocks:
items:
- description: module clock
clock-names:
items:
- const: mipi-cal
power-domains:
maxItems: 1
"#nvidia,mipi-calibrate-cells":
description: The number of cells in a MIPI calibration specifier.
Should be 1. The single cell specifies a bitmask of the pads that
need to be calibrated for a given device.
$ref: "/schemas/types.yaml#/definitions/uint32"
const: 1
additionalProperties: false
required:
- compatible
- reg
- clocks
- "#nvidia,mipi-calibrate-cells"
examples:
- |
#include <dt-bindings/clock/tegra114-car.h>
mipi@700e3000 {
compatible = "nvidia,tegra114-mipi";
reg = <0x700e3000 0x100>;
clocks = <&tegra_car TEGRA114_CLK_MIPI_CAL>;
clock-names = "mipi-cal";
#nvidia,mipi-calibrate-cells = <1>;
};
dsia: dsi@54300000 {
compatible = "nvidia,tegra114-dsi";
reg = <0x54300000 0x00040000>;
clocks = <&tegra_car TEGRA114_CLK_DSIA>,
<&tegra_car TEGRA114_CLK_DSIALP>,
<&tegra_car TEGRA114_CLK_PLL_D_OUT0>;
clock-names = "dsi", "lp", "parent";
resets = <&tegra_car 48>;
reset-names = "dsi";
nvidia,mipi-calibrate = <&mipi 0x060>; /* DSIA & DSIB pads */
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra124-dpaux.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra DisplayPort AUX Interface
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
description: |
The Tegra Display Port Auxiliary (DPAUX) pad controller manages two
pins which can be assigned to either the DPAUX channel or to an I2C
controller.
When configured for DisplayPort AUX operation, the DPAUX controller
can also be used to communicate with a DisplayPort device using the
AUX channel.
properties:
$nodename:
pattern: "^dpaux@[0-9a-f]+$"
compatible:
oneOf:
- enum:
- nvidia,tegra124-dpaux
- nvidia,tegra210-dpaux
- nvidia,tegra186-dpaux
- nvidia,tegra194-dpaux
- items:
- const: nvidia,tegra132-dpaux
- const: nvidia,tegra124-dpaux
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: clock input for the DPAUX hardware
- description: reference clock
clock-names:
items:
- const: dpaux
- const: parent
resets:
items:
- description: module reset
reset-names:
items:
- const: dpaux
power-domains:
maxItems: 1
i2c-bus:
description: Subnode where I2C slave devices are listed. This
subnode must be always present. If there are no I2C slave
devices, an empty node should be added. See ../../i2c/i2c.yaml
for more information.
type: object
aux-bus:
$ref: /schemas/display/dp-aux-bus.yaml#
vdd-supply:
description: phandle of a supply that powers the DisplayPort
link
patternProperties:
"^pinmux-[a-z0-9]+$":
description:
Since only three configurations are possible, only three child
nodes are needed to describe the pin mux'ing options for the
DPAUX pads. Furthermore, given that the pad functions are only
applicable to a single set of pads, the child nodes only need
to describe the pad group the functions are being applied to
rather than the individual pads.
type: object
properties:
groups:
const: dpaux-io
function:
enum:
- aux
- i2c
- off
additionalProperties: false
required:
- groups
- function
additionalProperties: false
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- resets
- reset-names
examples:
- |
#include <dt-bindings/clock/tegra210-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
dpaux: dpaux@545c0000 {
compatible = "nvidia,tegra210-dpaux";
reg = <0x545c0000 0x00040000>;
interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA210_CLK_DPAUX>,
<&tegra_car TEGRA210_CLK_PLL_DP>;
clock-names = "dpaux", "parent";
resets = <&tegra_car 181>;
reset-names = "dpaux";
power-domains = <&pd_sor>;
status = "disabled";
state_dpaux_aux: pinmux-aux {
groups = "dpaux-io";
function = "aux";
};
state_dpaux_i2c: pinmux-i2c {
groups = "dpaux-io";
function = "i2c";
};
state_dpaux_off: pinmux-off {
groups = "dpaux-io";
function = "off";
};
i2c-bus {
#address-cells = <1>;
#size-cells = <0>;
};
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra124-sor.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra SOR Output Encoder
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
description: |
The Serial Output Resource (SOR) can be used to drive HDMI, LVDS, eDP
and DP outputs.
properties:
$nodename:
pattern: "^sor@[0-9a-f]+$"
compatible:
oneOf:
- enum:
- nvidia,tegra124-sor
- nvidia,tegra210-sor
- nvidia,tegra210-sor1
- nvidia,tegra186-sor
- nvidia,tegra186-sor1
- nvidia,tegra194-sor
- items:
- const: nvidia,tegra132-sor
- const: nvidia,tegra124-sor
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
minItems: 5
maxItems: 6
clock-names:
minItems: 5
maxItems: 6
resets:
items:
- description: module reset
reset-names:
items:
- const: sor
power-domains:
maxItems: 1
avdd-io-hdmi-dp-supply:
description: I/O supply for HDMI/DP
vdd-hdmi-dp-pll-supply:
description: PLL supply for HDMI/DP
hdmi-supply:
description: +5.0V HDMI connector supply, required for HDMI
# Tegra186 and later
nvidia,interface:
description: index of the SOR interface
$ref: "/schemas/types.yaml#/definitions/uint32"
nvidia,ddc-i2c-bus:
description: phandle of an I2C controller used for DDC EDID
probing
$ref: "/schemas/types.yaml#/definitions/phandle"
nvidia,hpd-gpio:
description: specifies a GPIO used for hotplug detection
maxItems: 1
nvidia,edid:
description: supplies a binary EDID blob
$ref: "/schemas/types.yaml#/definitions/uint8-array"
nvidia,panel:
description: phandle of a display panel, required for eDP
$ref: "/schemas/types.yaml#/definitions/phandle"
nvidia,xbar-cfg:
description: 5 cells containing the crossbar configuration.
Each lane of the SOR, identified by the cell's index, is
mapped via the crossbar to the pad specified by the cell's
value.
$ref: "/schemas/types.yaml#/definitions/uint32-array"
# optional when driving an eDP output
nvidia,dpaux:
description: phandle to a DispayPort AUX interface
$ref: "/schemas/types.yaml#/definitions/phandle"
allOf:
- if:
properties:
compatible:
contains:
enum:
- nvidia,tegra186-sor
- nvidia,tegra194-sor
then:
properties:
clocks:
items:
- description: clock input for the SOR hardware
- description: SOR output clock
- description: input for the pixel clock
- description: reference clock for the SOR clock
- description: safe reference clock for the SOR clock
during power up
- description: SOR pad output clock
clock-names:
items:
- const: sor
- enum:
- source # deprecated
- out
- const: parent
- const: dp
- const: safe
- const: pad
else:
properties:
clocks:
items:
- description: clock input for the SOR hardware
- description: SOR output clock
- description: input for the pixel clock
- description: reference clock for the SOR clock
- description: safe reference clock for the SOR clock
during power up
clock-names:
items:
- const: sor
- enum:
- source # deprecated
- out
- const: parent
- const: dp
- const: safe
additionalProperties: false
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- resets
- reset-names
- avdd-io-hdmi-dp-supply
- vdd-hdmi-dp-pll-supply
examples:
- |
#include <dt-bindings/clock/tegra210-car.h>
#include <dt-bindings/gpio/tegra-gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
sor0: sor@54540000 {
compatible = "nvidia,tegra210-sor";
reg = <0x54540000 0x00040000>;
interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA210_CLK_SOR0>,
<&tegra_car TEGRA210_CLK_SOR0_OUT>,
<&tegra_car TEGRA210_CLK_PLL_D_OUT0>,
<&tegra_car TEGRA210_CLK_PLL_DP>,
<&tegra_car TEGRA210_CLK_SOR_SAFE>;
clock-names = "sor", "out", "parent", "dp", "safe";
resets = <&tegra_car 182>;
reset-names = "sor";
pinctrl-0 = <&state_dpaux_aux>;
pinctrl-1 = <&state_dpaux_i2c>;
pinctrl-2 = <&state_dpaux_off>;
pinctrl-names = "aux", "i2c", "off";
power-domains = <&pd_sor>;
avdd-io-hdmi-dp-supply = <&avdd_1v05>;
vdd-hdmi-dp-pll-supply = <&vdd_1v8>;
hdmi-supply = <&vdd_hdmi>;
nvidia,ddc-i2c-bus = <&hdmi_ddc>;
nvidia,hpd-gpio = <&gpio TEGRA_GPIO(CC, 1) GPIO_ACTIVE_LOW>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra124-vic.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra Video Image Composer
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^vic@[0-9a-f]+$"
compatible:
oneOf:
- enum:
- nvidia,tegra124-vic
- nvidia,tegra210-vic
- nvidia,tegra186-vic
- nvidia,tegra194-vic
- nvidia,tegra234-vic
- items:
- const: nvidia,tegra132-vic
- const: nvidia,tegra124-vic
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: clock input for the VIC hardware
clock-names:
items:
- const: vic
resets:
items:
- description: module reset
reset-names:
items:
- const: vic
power-domains:
maxItems: 1
iommus:
maxItems: 1
interconnects:
description: Description of the interconnect paths for the VIC;
see ../interconnect/interconnect.txt for details.
items:
- description: memory read client for VIC
- description: memory write client for VIC
interconnect-names:
items:
- const: dma-mem # read
- const: write
dma-coherent: true
additionalProperties: false
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra186-dc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra186 (and later) Display Controller
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^display@[0-9a-f]+$"
compatible:
enum:
- nvidia,tegra186-dc
- nvidia,tegra194-dc
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: display controller pixel clock
clock-names:
items:
- const: dc
resets:
items:
- description: display controller reset
reset-names:
items:
- const: dc
power-domains:
maxItems: 1
iommus:
maxItems: 1
interconnects:
description: Description of the interconnect paths for the
display controller; see ../interconnect/interconnect.txt
for details.
interconnect-names:
items:
- const: dma-mem # read-0
- const: read-1
nvidia,outputs:
description: A list of phandles of outputs that this display
controller can drive.
$ref: "/schemas/types.yaml#/definitions/phandle-array"
nvidia,head:
description: The number of the display controller head. This
is used to setup the various types of output to receive
video data from the given head.
$ref: "/schemas/types.yaml#/definitions/uint32"
additionalProperties: false
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- resets
- reset-names
- power-domains
- nvidia,outputs
- nvidia,head
# see nvidia,tegra186-display.yaml for examples
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra186-dsi-padctl.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra MIPI DSI pad controller
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^padctl@[0-9a-f]+$"
compatible:
const: nvidia,tegra186-dsi-padctl
reg:
maxItems: 1
resets:
items:
- description: module reset
reset-names:
items:
- const: dsi
allOf:
- $ref: "/schemas/reset/reset.yaml"
additionalProperties: false
examples:
- |
#include <dt-bindings/reset/tegra186-reset.h>
padctl@15880000 {
compatible = "nvidia,tegra186-dsi-padctl";
reg = <0x15880000 0x10000>;
resets = <&bpmp TEGRA186_RESET_DSI>;
reset-names = "dsi";
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-dc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra Display Controller
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^dc@[0-9a-f]+$"
compatible:
oneOf:
- enum:
- nvidia,tegra20-dc
- nvidia,tegra30-dc
- nvidia,tegra114-dc
- nvidia,tegra124-dc
- nvidia,tegra210-dc
- items:
- const: nvidia,tegra124-dc
- const: nvidia,tegra132-dc
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
minItems: 1
items:
- description: display controller pixel clock
- description: parent clock # optional
clock-names:
minItems: 1
items:
- const: dc
- const: parent # optional
resets:
items:
- description: module reset
reset-names:
items:
- const: dc
interconnect-names: true
interconnects: true
iommus:
maxItems: 1
operating-points-v2:
$ref: "/schemas/types.yaml#/definitions/phandle"
power-domains:
items:
- description: phandle to the core power domain
memory-region: true
nvidia,head:
$ref: /schemas/types.yaml#/definitions/uint32
description: The number of the display controller head. This is used to setup the various
types of output to receive video data from the given head.
nvidia,outputs:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: A list of phandles of outputs that this display controller can drive.
rgb:
type: object
allOf:
- if:
properties:
compatible:
contains:
enum:
- nvidia,tegra20-dc
- nvidia,tegra30-dc
- nvidia,tegra114-dc
then:
properties:
interconnects:
items:
- description: window A memory client
- description: window B memory client
- description: window B memory client (vertical filter)
- description: window C memory client
- description: cursor memory client
interconnect-names:
items:
- const: wina
- const: winb
- const: winb-vfilter
- const: winc
- const: cursor
rgb:
description: Each display controller node has a child node, named "rgb", that represents
the RGB output associated with the controller.
type: object
properties:
nvidia,ddc-i2c-bus:
$ref: /schemas/types.yaml#/definitions/phandle
description: phandle of an I2C controller used for DDC EDID probing
nvidia,hpd-gpio:
description: specifies a GPIO used for hotplug detection
maxItems: 1
nvidia,edid:
$ref: /schemas/types.yaml#/definitions/uint8-array
description: supplies a binary EDID blob
nvidia,panel:
$ref: /schemas/types.yaml#/definitions/phandle
description: phandle of a display panel
- if:
properties:
compatible:
contains:
enum:
- nvidia,tegra124-dc
then:
properties:
interconnects:
minItems: 4
items:
- description: window A memory client
- description: window B memory client
- description: window C memory client
- description: cursor memory client
- description: window D memory client
- description: window T memory client
interconnect-names:
minItems: 4
items:
- const: wina
- const: winb
- const: winc
- const: cursor
- const: wind
- const: wint
additionalProperties: false
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- resets
- reset-names
examples:
- |
#include <dt-bindings/clock/tegra20-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
dc@54200000 {
compatible = "nvidia,tegra20-dc";
reg = <0x54200000 0x00040000>;
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA20_CLK_DISP1>;
clock-names = "dc";
resets = <&tegra_car 27>;
reset-names = "dc";
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-dsi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra Display Serial Interface
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
compatible:
oneOf:
- enum:
- nvidia,tegra20-dsi
- nvidia,tegra30-dsi
- nvidia,tegra114-dsi
- nvidia,tegra124-dsi
- nvidia,tegra210-dsi
- nvidia,tegra186-dsi
- items:
- const: nvidia,tegra132-dsi
- const: nvidia,tegra124-dsi
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
minItems: 2
maxItems: 3
clock-names:
minItems: 2
maxItems: 3
resets:
items:
- description: module reset
reset-names:
items:
- const: dsi
operating-points-v2:
$ref: "/schemas/types.yaml#/definitions/phandle"
power-domains:
maxItems: 1
avdd-dsi-csi-supply:
description: phandle of a supply that powers the DSI controller
nvidia,mipi-calibrate:
description: Should contain a phandle and a specifier specifying
which pads are used by this DSI output and need to be
calibrated. See nvidia,tegra114-mipi.yaml for details.
$ref: "/schemas/types.yaml#/definitions/phandle-array"
nvidia,ddc-i2c-bus:
description: phandle of an I2C controller used for DDC EDID
probing
$ref: "/schemas/types.yaml#/definitions/phandle"
nvidia,hpd-gpio:
description: specifies a GPIO used for hotplug detection
maxItems: 1
nvidia,edid:
description: supplies a binary EDID blob
$ref: "/schemas/types.yaml#/definitions/uint8-array"
nvidia,panel:
description: phandle of a display panel
$ref: "/schemas/types.yaml#/definitions/phandle"
nvidia,ganged-mode:
description: contains a phandle to a second DSI controller to
gang up with in order to support up to 8 data lanes
$ref: "/schemas/types.yaml#/definitions/phandle"
allOf:
- $ref: "../dsi-controller.yaml#"
- if:
properties:
compatible:
contains:
enum:
- nvidia,tegra20-dsi
- nvidia,tegra30-dsi
then:
properties:
clocks:
items:
- description: DSI module clock
- description: input for the pixel clock
clock-names:
items:
- const: dsi
- const: parent
else:
properties:
clocks:
items:
- description: DSI module clock
- description: low-power module clock
- description: input for the pixel clock
clock-names:
items:
- const: dsi
- const: lp
- const: parent
- if:
properties:
compatible:
contains:
const: nvidia,tegra186-dsi
then:
required:
- interrupts
unevaluatedProperties: false
required:
- compatible
- reg
- clocks
- clock-names
- resets
- reset-names
examples:
- |
#include <dt-bindings/clock/tegra186-clock.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/power/tegra186-powergate.h>
#include <dt-bindings/reset/tegra186-reset.h>
dsi@15300000 {
compatible = "nvidia,tegra186-dsi";
reg = <0x15300000 0x10000>;
interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&bpmp TEGRA186_CLK_DSI>,
<&bpmp TEGRA186_CLK_DSIA_LP>,
<&bpmp TEGRA186_CLK_PLLD>;
clock-names = "dsi", "lp", "parent";
resets = <&bpmp TEGRA186_RESET_DSI>;
reset-names = "dsi";
power-domains = <&bpmp TEGRA186_POWER_DOMAIN_DISP>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-epp.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra Encoder Pre-Processor
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^epp@[0-9a-f]+$"
compatible:
enum:
- nvidia,tegra20-epp
- nvidia,tegra30-epp
- nvidia,tegra114-epp
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
resets:
items:
- description: module reset
reset-names:
items:
- const: epp
iommus:
maxItems: 1
interconnects:
maxItems: 4
interconnect-names:
maxItems: 4
operating-points-v2:
$ref: "/schemas/types.yaml#/definitions/phandle"
power-domains:
items:
- description: phandle to the core power domain
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/tegra20-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
epp@540c0000 {
compatible = "nvidia,tegra20-epp";
reg = <0x540c0000 0x00040000>;
interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA20_CLK_EPP>;
resets = <&tegra_car 19>;
reset-names = "epp";
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-gr2d.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA 2D graphics engine
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^gr2d@[0-9a-f]+$"
compatible:
enum:
- nvidia,tegra20-gr2d
- nvidia,tegra30-gr2d
- nvidia,tegra114-gr2d
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: module clock
resets:
items:
- description: module reset
- description: memory client hotflush reset
reset-names:
items:
- const: 2d
- const: mc
iommus:
maxItems: 1
interconnects:
maxItems: 4
interconnect-names:
maxItems: 4
operating-points-v2:
$ref: "/schemas/types.yaml#/definitions/phandle"
power-domains:
items:
- description: phandle to the HEG or core power domain
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/tegra20-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/memory/tegra20-mc.h>
gr2d@54140000 {
compatible = "nvidia,tegra20-gr2d";
reg = <0x54140000 0x00040000>;
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA20_CLK_GR2D>;
resets = <&tegra_car 21>, <&mc TEGRA20_MC_RESET_2D>;
reset-names = "2d", "mc";
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-gr3d.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA 3D graphics engine
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^gr3d@[0-9a-f]+$"
compatible:
enum:
- nvidia,tegra20-gr3d
- nvidia,tegra30-gr3d
- nvidia,tegra114-gr3d
reg:
maxItems: 1
clocks:
minItems: 1
maxItems: 2
clock-names:
minItems: 1
maxItems: 2
resets:
minItems: 2
maxItems: 4
reset-names:
minItems: 2
maxItems: 4
iommus:
minItems: 1
maxItems: 2
interconnects:
minItems: 4
maxItems: 10
interconnect-names:
minItems: 4
maxItems: 10
operating-points-v2:
$ref: "/schemas/types.yaml#/definitions/phandle"
power-domains:
minItems: 1
maxItems: 2
power-domain-names:
minItems: 2
maxItems: 2
allOf:
- if:
properties:
compatible:
contains:
const: nvidia,tegra20-gr2d
then:
properties:
clocks:
items:
- description: module clock
clock-names:
items:
- const: 3d
resets:
items:
- description: module reset
- description: memory client hotflush reset
reset-names:
items:
- const: 3d
- const: mc
iommus:
maxItems: 1
interconnects:
minItems: 4
maxItems: 4
interconnect-names:
minItems: 4
maxItems: 4
power-domains:
items:
- description: phandle to the TD power domain
- if:
properties:
compatible:
contains:
const: nvidia,tegra30-gr3d
then:
properties:
clocks:
items:
- description: primary module clock
- description: secondary module clock
clock-names:
items:
- const: 3d
- const: 3d2
resets:
items:
- description: primary module reset
- description: secondary module reset
- description: primary memory client hotflush reset
- description: secondary memory client hotflush reset
reset-names:
items:
- const: 3d
- const: 3d2
- const: mc
- const: mc2
iommus:
minItems: 2
maxItems: 2
interconnects:
minItems: 8
maxItems: 8
interconnect-names:
minItems: 8
maxItems: 8
power-domains:
items:
- description: phandle to the TD power domain
- description: phandle to the TD2 power domain
power-domain-names:
items:
- const: 3d0
- const: 3d1
dependencies:
power-domains: [ power-domain-names ]
- if:
properties:
compatible:
contains:
const: nvidia,tegra114-gr2d
then:
properties:
clocks:
items:
- description: module clock
clock-names:
items:
- const: 3d
resets:
items:
- description: module reset
- description: memory client hotflush reset
reset-names:
items:
- const: 3d
- const: mc
iommus:
maxItems: 1
interconnects:
minItems: 10
maxItems: 10
interconnect-names:
minItems: 10
maxItems: 10
power-domains:
items:
- description: phandle to the TD power domain
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/tegra20-car.h>
#include <dt-bindings/memory/tegra20-mc.h>
gr3d@54180000 {
compatible = "nvidia,tegra20-gr3d";
reg = <0x54180000 0x00040000>;
clocks = <&tegra_car TEGRA20_CLK_GR3D>;
resets = <&tegra_car 24>, <&mc TEGRA20_MC_RESET_3D>;
reset-names = "3d", "mc";
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-hdmi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra HDMI Output Encoder
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^hdmi@[0-9a-f]+$"
compatible:
oneOf:
- enum:
- nvidia,tegra20-hdmi
- nvidia,tegra30-hdmi
- nvidia,tegra114-hdmi
- nvidia,tegra124-hdmi
- items:
- const: nvidia,tegra132-hdmi
- const: nvidia,tegra124-hdmi
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: module clock
- description: parent clock
clock-names:
items:
- const: hdmi
- const: parent
resets:
items:
- description: module reset
reset-names:
items:
- const: hdmi
operating-points-v2:
$ref: "/schemas/types.yaml#/definitions/phandle"
power-domains:
items:
- description: phandle to the core power domain
hdmi-supply:
description: supply for the +5V HDMI connector pin
vdd-supply:
description: regulator for supply voltage
pll-supply:
description: regulator for PLL
nvidia,ddc-i2c-bus:
description: phandle of an I2C controller used for DDC EDID
probing
$ref: "/schemas/types.yaml#/definitions/phandle"
nvidia,hpd-gpio:
description: specifies a GPIO used for hotplug detection
maxItems: 1
nvidia,edid:
description: supplies a binary EDID blob
$ref: "/schemas/types.yaml#/definitions/uint8-array"
nvidia,panel:
description: phandle of a display panel
$ref: "/schemas/types.yaml#/definitions/phandle"
"#sound-dai-cells":
const: 0
additionalProperties: false
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- resets
- reset-names
- pll-supply
- vdd-supply
- nvidia,ddc-i2c-bus
- nvidia,hpd-gpio
examples:
- |
#include <dt-bindings/clock/tegra124-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/gpio/tegra-gpio.h>
hdmi@54280000 {
compatible = "nvidia,tegra124-hdmi";
reg = <0x54280000 0x00040000>;
interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_HDMI>,
<&tegra_car TEGRA124_CLK_PLL_D2_OUT0>;
clock-names = "hdmi", "parent";
resets = <&tegra_car 51>;
reset-names = "hdmi";
hdmi-supply = <&vdd_5v0_hdmi>;
pll-supply = <&vdd_hdmi_pll>;
vdd-supply = <&vdd_3v3_hdmi>;
nvidia,ddc-i2c-bus = <&hdmi_ddc>;
nvidia,hpd-gpio = <&gpio TEGRA_GPIO(N, 7) GPIO_ACTIVE_HIGH>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-isp.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra ISP processor
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
compatible:
enum:
- nvidia,tegra20-isp
- nvidia,tegra30-isp
- nvidia,tegra210-isp
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: module clock
resets:
items:
- description: module reset
reset-names:
items:
- const: isp
iommus:
maxItems: 1
interconnects:
items:
- description: memory write client
interconnect-names:
items:
- const: dma-mem # write
power-domains:
items:
- description: phandle to the VENC or core power domain
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/tegra20-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
isp@54100000 {
compatible = "nvidia,tegra20-isp";
reg = <0x54100000 0x00040000>;
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA20_CLK_ISP>;
resets = <&tegra_car 23>;
reset-names = "isp";
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-mpe.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra Video Encoder
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^mpe@[0-9a-f]+$"
compatible:
enum:
- nvidia,tegra20-mpe
- nvidia,tegra30-mpe
- nvidia,tegra114-mpe
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: module clock
resets:
items:
- description: module reset
reset-names:
items:
- const: mpe
iommus:
maxItems: 1
interconnects:
minItems: 6
maxItems: 6
interconnect-names:
minItems: 6
maxItems: 6
operating-points-v2:
$ref: "/schemas/types.yaml#/definitions/phandle"
power-domains:
items:
- description: phandle to the MPE power domain
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/tegra20-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
mpe@54040000 {
compatible = "nvidia,tegra20-mpe";
reg = <0x54040000 0x00040000>;
interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA20_CLK_MPE>;
resets = <&tegra_car 60>;
reset-names = "mpe";
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-tvo.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra TV Encoder Output
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^tvo@[0-9a-f]+$"
compatible:
enum:
- nvidia,tegra20-tvo
- nvidia,tegra30-tvo
- nvidia,tegra114-tvo
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: module clock
operating-points-v2:
$ref: "/schemas/types.yaml#/definitions/phandle"
power-domains:
items:
- description: phandle to the core power domain
additionalProperties: false
required:
- compatible
- reg
- interrupts
- clocks
examples:
- |
#include <dt-bindings/clock/tegra20-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
tvo@542c0000 {
compatible = "nvidia,tegra20-tvo";
reg = <0x542c0000 0x00040000>;
interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA20_CLK_TVO>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-vi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra Video Input controller
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^vi@[0-9a-f]+$"
compatible:
oneOf:
- const: nvidia,tegra20-vi
- const: nvidia,tegra30-vi
- const: nvidia,tegra114-vi
- const: nvidia,tegra124-vi
- items:
- const: nvidia,tegra132-vi
- const: nvidia,tegra124-vi
- const: nvidia,tegra210-vi
- const: nvidia,tegra186-vi
- const: nvidia,tegra194-vi
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
resets:
items:
- description: module reset
reset-names:
items:
- const: vi
iommus:
maxItems: 1
interconnects:
minItems: 4
maxItems: 5
interconnect-names:
minItems: 4
maxItems: 5
operating-points-v2:
$ref: "/schemas/types.yaml#/definitions/phandle"
power-domains:
items:
- description: phandle to the VENC power domain
"#address-cells":
const: 1
"#size-cells":
const: 1
ranges:
maxItems: 1
avdd-dsi-csi-supply:
description: DSI/CSI power supply. Must supply 1.2 V.
patternProperties:
"^csi@[0-9a-f]+$":
type: object
additionalProperties: false
required:
- compatible
- reg
- interrupts
- clocks
allOf:
- if:
properties:
compatible:
contains:
enum:
- nvidia,tegra20-vi
- nvidia,tegra30-vi
- nvidia,tegra114-vi
- nvidia,tegra124-vi
then:
required:
- resets
- reset-names
else:
required:
- power-domains
examples:
- |
#include <dt-bindings/clock/tegra20-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
vi@54080000 {
compatible = "nvidia,tegra20-vi";
reg = <0x54080000 0x00040000>;
interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA20_CLK_VI>;
resets = <&tegra_car 100>;
reset-names = "vi";
};
- |
#include <dt-bindings/clock/tegra210-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
vi@54080000 {
compatible = "nvidia,tegra210-vi";
reg = <0x54080000 0x00000700>;
interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
assigned-clocks = <&tegra_car TEGRA210_CLK_VI>;
assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_C4_OUT0>;
clocks = <&tegra_car TEGRA210_CLK_VI>;
power-domains = <&pd_venc>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x54080000 0x2000>;
csi@838 {
compatible = "nvidia,tegra210-csi";
reg = <0x838 0x1300>;
assigned-clocks = <&tegra_car TEGRA210_CLK_CILAB>,
<&tegra_car TEGRA210_CLK_CILCD>,
<&tegra_car TEGRA210_CLK_CILE>,
<&tegra_car TEGRA210_CLK_CSI_TPG>;
assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_P>,
<&tegra_car TEGRA210_CLK_PLL_P>,
<&tegra_car TEGRA210_CLK_PLL_P>;
assigned-clock-rates = <102000000>,
<102000000>,
<102000000>,
<972000000>;
clocks = <&tegra_car TEGRA210_CLK_CSI>,
<&tegra_car TEGRA210_CLK_CILAB>,
<&tegra_car TEGRA210_CLK_CILCD>,
<&tegra_car TEGRA210_CLK_CILE>,
<&tegra_car TEGRA210_CLK_CSI_TPG>;
clock-names = "csi", "cilab", "cilcd", "cile", "csi_tpg";
power-domains = <&pd_sor>;
};
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra210-csi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra CSI controller
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
$nodename:
pattern: "^csi@[0-9a-f]+$"
compatible:
enum:
- nvidia,tegra210-csi
reg:
maxItems: 1
clocks:
items:
- description: module clock
- description: A/B lanes clock
- description: C/D lanes clock
- description: E lane clock
- description: test pattern generator clock
clock-names:
items:
- const: csi
- const: cilab
- const: cilcd
- const: cile
- const: csi_tpg
power-domains:
maxItems: 1
additionalProperties: false
required:
- compatible
- reg
- clocks
- clock-names
- power-domains
# see nvidia,tegra20-vi.yaml for an example
Device tree binding for NVIDIA Tegra DPAUX pad controller
========================================================
The Tegra Display Port Auxiliary (DPAUX) pad controller manages two pins
which can be assigned to either the DPAUX channel or to an I2C
controller.
This document defines the device-specific binding for the DPAUX pad
controller. Refer to pinctrl-bindings.txt in this directory for generic
information about pin controller device tree bindings. Please refer to
the binding document ../display/tegra/nvidia,tegra20-host1x.txt for more
details on the DPAUX binding.
Pin muxing:
-----------
Child nodes contain the pinmux configurations following the conventions
from the pinctrl-bindings.txt document.
Since only three configurations are possible, only three child nodes are
needed to describe the pin mux'ing options for the DPAUX pads.
Furthermore, given that the pad functions are only applicable to a
single set of pads, the child nodes only need to describe the pad group
the functions are being applied to rather than the individual pads.
Required properties:
- groups: Must be "dpaux-io"
- function: Must be either "aux", "i2c" or "off".
Example:
--------
dpaux@545c0000 {
...
state_dpaux_aux: pinmux-aux {
groups = "dpaux-io";
function = "aux";
};
state_dpaux_i2c: pinmux-i2c {
groups = "dpaux-io";
function = "i2c";
};
state_dpaux_off: pinmux-off {
groups = "dpaux-io";
function = "off";
};
};
...
i2c@7000d100 {
...
pinctrl-0 = <&state_dpaux_i2c>;
pinctrl-1 = <&state_dpaux_off>;
pinctrl-names = "default", "idle";
};
...@@ -6748,7 +6748,7 @@ L: dri-devel@lists.freedesktop.org ...@@ -6748,7 +6748,7 @@ L: dri-devel@lists.freedesktop.org
L: linux-tegra@vger.kernel.org L: linux-tegra@vger.kernel.org
S: Supported S: Supported
T: git git://anongit.freedesktop.org/tegra/linux.git T: git git://anongit.freedesktop.org/tegra/linux.git
F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
F: Documentation/devicetree/bindings/gpu/host1x/ F: Documentation/devicetree/bindings/gpu/host1x/
F: drivers/gpu/drm/tegra/ F: drivers/gpu/drm/tegra/
F: drivers/gpu/host1x/ F: drivers/gpu/host1x/
...@@ -19682,7 +19682,7 @@ M: Sowjanya Komatineni <skomatineni@nvidia.com> ...@@ -19682,7 +19682,7 @@ M: Sowjanya Komatineni <skomatineni@nvidia.com>
L: linux-media@vger.kernel.org L: linux-media@vger.kernel.org
L: linux-tegra@vger.kernel.org L: linux-tegra@vger.kernel.org
S: Maintained S: Maintained
F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
F: drivers/staging/media/tegra-video/ F: drivers/staging/media/tegra-video/
TEGRA XUSB PADCTL DRIVER TEGRA XUSB PADCTL DRIVER
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/iommu.h> #include <linux/iommu.h>
#include <linux/interconnect.h> #include <linux/interconnect.h>
#include <linux/module.h> #include <linux/module.h>
......
...@@ -1381,6 +1381,7 @@ static const struct of_device_id host1x_drm_subdevs[] = { ...@@ -1381,6 +1381,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra194-sor", }, { .compatible = "nvidia,tegra194-sor", },
{ .compatible = "nvidia,tegra194-vic", }, { .compatible = "nvidia,tegra194-vic", },
{ .compatible = "nvidia,tegra194-nvdec", }, { .compatible = "nvidia,tegra194-nvdec", },
{ .compatible = "nvidia,tegra234-vic", },
{ /* sentinel */ } { /* sentinel */ }
}; };
......
...@@ -80,6 +80,7 @@ struct tegra_drm_context { ...@@ -80,6 +80,7 @@ struct tegra_drm_context {
/* Only used by new UAPI. */ /* Only used by new UAPI. */
struct xarray mappings; struct xarray mappings;
struct host1x_memory_context *memory_context;
}; };
struct tegra_drm_client_ops { struct tegra_drm_client_ops {
...@@ -91,12 +92,22 @@ struct tegra_drm_client_ops { ...@@ -91,12 +92,22 @@ struct tegra_drm_client_ops {
int (*submit)(struct tegra_drm_context *context, int (*submit)(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm, struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file); struct drm_file *file);
int (*get_streamid_offset)(struct tegra_drm_client *client, u32 *offset);
int (*can_use_memory_ctx)(struct tegra_drm_client *client, bool *supported);
}; };
int tegra_drm_submit(struct tegra_drm_context *context, int tegra_drm_submit(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm, struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file); struct drm_file *file);
static inline int
tegra_drm_get_streamid_offset_thi(struct tegra_drm_client *client, u32 *offset)
{
*offset = 0x30;
return 0;
}
struct tegra_drm_client { struct tegra_drm_client {
struct host1x_client base; struct host1x_client base;
struct list_head list; struct list_head list;
......
...@@ -48,6 +48,14 @@ static int falcon_copy_chunk(struct falcon *falcon, ...@@ -48,6 +48,14 @@ static int falcon_copy_chunk(struct falcon *falcon,
if (target == FALCON_MEMORY_IMEM) if (target == FALCON_MEMORY_IMEM)
cmd |= FALCON_DMATRFCMD_IMEM; cmd |= FALCON_DMATRFCMD_IMEM;
/*
* Use second DMA context (i.e. the one for firmware). Strictly
* speaking, at this point both DMA contexts point to the firmware
* stream ID, but this register's value will be reused by the firmware
* for later DMA transactions, so we need to use the correct value.
*/
cmd |= FALCON_DMATRFCMD_DMACTX(1);
falcon_writel(falcon, offset, FALCON_DMATRFMOFFS); falcon_writel(falcon, offset, FALCON_DMATRFMOFFS);
falcon_writel(falcon, base, FALCON_DMATRFFBOFFS); falcon_writel(falcon, base, FALCON_DMATRFFBOFFS);
falcon_writel(falcon, cmd, FALCON_DMATRFCMD); falcon_writel(falcon, cmd, FALCON_DMATRFCMD);
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#define FALCON_DMATRFCMD_IDLE (1 << 1) #define FALCON_DMATRFCMD_IDLE (1 << 1)
#define FALCON_DMATRFCMD_IMEM (1 << 4) #define FALCON_DMATRFCMD_IMEM (1 << 4)
#define FALCON_DMATRFCMD_SIZE_256B (6 << 8) #define FALCON_DMATRFCMD_SIZE_256B (6 << 8)
#define FALCON_DMATRFCMD_DMACTX(v) (((v) & 0x7) << 12)
#define FALCON_DMATRFFBOFFS 0x0000111c #define FALCON_DMATRFFBOFFS 0x0000111c
......
...@@ -704,14 +704,23 @@ static int tegra_gem_prime_vmap(struct dma_buf *buf, struct iosys_map *map) ...@@ -704,14 +704,23 @@ static int tegra_gem_prime_vmap(struct dma_buf *buf, struct iosys_map *map)
{ {
struct drm_gem_object *gem = buf->priv; struct drm_gem_object *gem = buf->priv;
struct tegra_bo *bo = to_tegra_bo(gem); struct tegra_bo *bo = to_tegra_bo(gem);
void *vaddr;
iosys_map_set_vaddr(map, bo->vaddr); vaddr = tegra_bo_mmap(&bo->base);
if (IS_ERR(vaddr))
return PTR_ERR(vaddr);
iosys_map_set_vaddr(map, vaddr);
return 0; return 0;
} }
static void tegra_gem_prime_vunmap(struct dma_buf *buf, struct iosys_map *map) static void tegra_gem_prime_vunmap(struct dma_buf *buf, struct iosys_map *map)
{ {
struct drm_gem_object *gem = buf->priv;
struct tegra_bo *bo = to_tegra_bo(gem);
tegra_bo_munmap(&bo->base, map->vaddr);
} }
static const struct dma_buf_ops tegra_gem_prime_dmabuf_ops = { static const struct dma_buf_ops tegra_gem_prime_dmabuf_ops = {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/host1x.h> #include <linux/host1x.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/host1x.h> #include <linux/host1x.h>
#include <linux/iommu.h> #include <linux/iommu.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -21,6 +22,8 @@ ...@@ -21,6 +22,8 @@
#include "falcon.h" #include "falcon.h"
#include "vic.h" #include "vic.h"
#define NVDEC_TFBIF_TRANSCFG 0x2c44
struct nvdec_config { struct nvdec_config {
const char *firmware; const char *firmware;
unsigned int version; unsigned int version;
...@@ -63,7 +66,7 @@ static int nvdec_boot(struct nvdec *nvdec) ...@@ -63,7 +66,7 @@ static int nvdec_boot(struct nvdec *nvdec)
u32 value; u32 value;
value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) | TRANSCFG_ATT(0, TRANSCFG_SID_HW); value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) | TRANSCFG_ATT(0, TRANSCFG_SID_HW);
nvdec_writel(nvdec, value, VIC_TFBIF_TRANSCFG); nvdec_writel(nvdec, value, NVDEC_TFBIF_TRANSCFG);
if (spec->num_ids > 0) { if (spec->num_ids > 0) {
value = spec->ids[0] & 0xffff; value = spec->ids[0] & 0xffff;
...@@ -304,10 +307,19 @@ static void nvdec_close_channel(struct tegra_drm_context *context) ...@@ -304,10 +307,19 @@ static void nvdec_close_channel(struct tegra_drm_context *context)
host1x_channel_put(context->channel); host1x_channel_put(context->channel);
} }
static int nvdec_can_use_memory_ctx(struct tegra_drm_client *client, bool *supported)
{
*supported = true;
return 0;
}
static const struct tegra_drm_client_ops nvdec_ops = { static const struct tegra_drm_client_ops nvdec_ops = {
.open_channel = nvdec_open_channel, .open_channel = nvdec_open_channel,
.close_channel = nvdec_close_channel, .close_channel = nvdec_close_channel,
.submit = tegra_drm_submit, .submit = tegra_drm_submit,
.get_streamid_offset = tegra_drm_get_streamid_offset_thi,
.can_use_memory_ctx = nvdec_can_use_memory_ctx,
}; };
#define NVIDIA_TEGRA_210_NVDEC_FIRMWARE "nvidia/tegra210/nvdec.bin" #define NVIDIA_TEGRA_210_NVDEC_FIRMWARE "nvidia/tegra210/nvdec.bin"
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
*/ */
#include <linux/dma-mapping.h>
#include <linux/iommu.h> #include <linux/iommu.h>
#include <linux/interconnect.h> #include <linux/interconnect.h>
......
...@@ -498,6 +498,9 @@ static void release_job(struct host1x_job *job) ...@@ -498,6 +498,9 @@ static void release_job(struct host1x_job *job)
struct tegra_drm_submit_data *job_data = job->user_data; struct tegra_drm_submit_data *job_data = job->user_data;
u32 i; u32 i;
if (job->memory_context)
host1x_memory_context_put(job->memory_context);
for (i = 0; i < job_data->num_used_mappings; i++) for (i = 0; i < job_data->num_used_mappings; i++)
tegra_drm_mapping_put(job_data->used_mappings[i].mapping); tegra_drm_mapping_put(job_data->used_mappings[i].mapping);
...@@ -588,11 +591,51 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data, ...@@ -588,11 +591,51 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,
goto put_job; goto put_job;
} }
if (context->client->ops->get_streamid_offset) {
err = context->client->ops->get_streamid_offset(
context->client, &job->engine_streamid_offset);
if (err) {
SUBMIT_ERR(context, "failed to get streamid offset: %d", err);
goto unpin_job;
}
}
if (context->memory_context && context->client->ops->can_use_memory_ctx) {
bool supported;
err = context->client->ops->can_use_memory_ctx(context->client, &supported);
if (err) {
SUBMIT_ERR(context, "failed to detect if engine can use memory context: %d", err);
goto unpin_job;
}
if (supported) {
job->memory_context = context->memory_context;
host1x_memory_context_get(job->memory_context);
}
} else if (context->client->ops->get_streamid_offset) {
#ifdef CONFIG_IOMMU_API
struct iommu_fwspec *spec;
/*
* Job submission will need to temporarily change stream ID,
* so need to tell it what to change it back to.
*/
spec = dev_iommu_fwspec_get(context->client->base.dev);
if (spec && spec->num_ids > 0)
job->engine_fallback_streamid = spec->ids[0] & 0xffff;
else
job->engine_fallback_streamid = 0x7f;
#else
job->engine_fallback_streamid = 0x7f;
#endif
}
/* Boot engine. */ /* Boot engine. */
err = pm_runtime_resume_and_get(context->client->base.dev); err = pm_runtime_resume_and_get(context->client->base.dev);
if (err < 0) { if (err < 0) {
SUBMIT_ERR(context, "could not power up engine: %d", err); SUBMIT_ERR(context, "could not power up engine: %d", err);
goto unpin_job; goto put_memory_context;
} }
job->user_data = job_data; job->user_data = job_data;
...@@ -627,6 +670,9 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data, ...@@ -627,6 +670,9 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,
goto put_job; goto put_job;
put_memory_context:
if (job->memory_context)
host1x_memory_context_put(job->memory_context);
unpin_job: unpin_job:
host1x_job_unpin(job); host1x_job_unpin(job);
put_job: put_job:
......
...@@ -33,6 +33,9 @@ static void tegra_drm_channel_context_close(struct tegra_drm_context *context) ...@@ -33,6 +33,9 @@ static void tegra_drm_channel_context_close(struct tegra_drm_context *context)
struct tegra_drm_mapping *mapping; struct tegra_drm_mapping *mapping;
unsigned long id; unsigned long id;
if (context->memory_context)
host1x_memory_context_put(context->memory_context);
xa_for_each(&context->mappings, id, mapping) xa_for_each(&context->mappings, id, mapping)
tegra_drm_mapping_put(mapping); tegra_drm_mapping_put(mapping);
...@@ -72,6 +75,7 @@ static struct tegra_drm_client *tegra_drm_find_client(struct tegra_drm *tegra, u ...@@ -72,6 +75,7 @@ static struct tegra_drm_client *tegra_drm_find_client(struct tegra_drm *tegra, u
int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_file *file) int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_file *file)
{ {
struct host1x *host = tegra_drm_to_host1x(drm->dev_private);
struct tegra_drm_file *fpriv = file->driver_priv; struct tegra_drm_file *fpriv = file->driver_priv;
struct tegra_drm *tegra = drm->dev_private; struct tegra_drm *tegra = drm->dev_private;
struct drm_tegra_channel_open *args = data; struct drm_tegra_channel_open *args = data;
...@@ -102,10 +106,36 @@ int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_ ...@@ -102,10 +106,36 @@ int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_
} }
} }
/* Only allocate context if the engine supports context isolation. */
if (device_iommu_mapped(client->base.dev) && client->ops->can_use_memory_ctx) {
bool supported;
err = client->ops->can_use_memory_ctx(client, &supported);
if (err)
goto put_channel;
if (supported)
context->memory_context = host1x_memory_context_alloc(
host, get_task_pid(current, PIDTYPE_TGID));
if (IS_ERR(context->memory_context)) {
if (PTR_ERR(context->memory_context) != -EOPNOTSUPP) {
err = PTR_ERR(context->memory_context);
goto put_channel;
} else {
/*
* OK, HW does not support contexts or contexts
* are disabled.
*/
context->memory_context = NULL;
}
}
}
err = xa_alloc(&fpriv->contexts, &args->context, context, XA_LIMIT(1, U32_MAX), err = xa_alloc(&fpriv->contexts, &args->context, context, XA_LIMIT(1, U32_MAX),
GFP_KERNEL); GFP_KERNEL);
if (err < 0) if (err < 0)
goto put_channel; goto put_memctx;
context->client = client; context->client = client;
xa_init_flags(&context->mappings, XA_FLAGS_ALLOC1); xa_init_flags(&context->mappings, XA_FLAGS_ALLOC1);
...@@ -118,6 +148,9 @@ int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_ ...@@ -118,6 +148,9 @@ int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_
return 0; return 0;
put_memctx:
if (context->memory_context)
host1x_memory_context_put(context->memory_context);
put_channel: put_channel:
host1x_channel_put(context->channel); host1x_channel_put(context->channel);
free: free:
...@@ -156,6 +189,7 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f ...@@ -156,6 +189,7 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f
struct tegra_drm_mapping *mapping; struct tegra_drm_mapping *mapping;
struct tegra_drm_context *context; struct tegra_drm_context *context;
enum dma_data_direction direction; enum dma_data_direction direction;
struct device *mapping_dev;
int err = 0; int err = 0;
if (args->flags & ~DRM_TEGRA_CHANNEL_MAP_READ_WRITE) if (args->flags & ~DRM_TEGRA_CHANNEL_MAP_READ_WRITE)
...@@ -177,6 +211,11 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f ...@@ -177,6 +211,11 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f
kref_init(&mapping->ref); kref_init(&mapping->ref);
if (context->memory_context)
mapping_dev = &context->memory_context->dev;
else
mapping_dev = context->client->base.dev;
mapping->bo = tegra_gem_lookup(file, args->handle); mapping->bo = tegra_gem_lookup(file, args->handle);
if (!mapping->bo) { if (!mapping->bo) {
err = -EINVAL; err = -EINVAL;
...@@ -201,7 +240,7 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f ...@@ -201,7 +240,7 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f
goto put_gem; goto put_gem;
} }
mapping->map = host1x_bo_pin(context->client->base.dev, mapping->bo, direction, NULL); mapping->map = host1x_bo_pin(mapping_dev, mapping->bo, direction, NULL);
if (IS_ERR(mapping->map)) { if (IS_ERR(mapping->map)) {
err = PTR_ERR(mapping->map); err = PTR_ERR(mapping->map);
goto put_gem; goto put_gem;
......
...@@ -38,6 +38,8 @@ struct vic { ...@@ -38,6 +38,8 @@ struct vic {
struct clk *clk; struct clk *clk;
struct reset_control *rst; struct reset_control *rst;
bool can_use_context;
/* Platform configuration */ /* Platform configuration */
const struct vic_config *config; const struct vic_config *config;
}; };
...@@ -229,28 +231,38 @@ static int vic_load_firmware(struct vic *vic) ...@@ -229,28 +231,38 @@ static int vic_load_firmware(struct vic *vic)
{ {
struct host1x_client *client = &vic->client.base; struct host1x_client *client = &vic->client.base;
struct tegra_drm *tegra = vic->client.drm; struct tegra_drm *tegra = vic->client.drm;
static DEFINE_MUTEX(lock);
u32 fce_bin_data_offset;
dma_addr_t iova; dma_addr_t iova;
size_t size; size_t size;
void *virt; void *virt;
int err; int err;
if (vic->falcon.firmware.virt) mutex_lock(&lock);
return 0;
if (vic->falcon.firmware.virt) {
err = 0;
goto unlock;
}
err = falcon_read_firmware(&vic->falcon, vic->config->firmware); err = falcon_read_firmware(&vic->falcon, vic->config->firmware);
if (err < 0) if (err < 0)
return err; goto unlock;
size = vic->falcon.firmware.size; size = vic->falcon.firmware.size;
if (!client->group) { if (!client->group) {
virt = dma_alloc_coherent(vic->dev, size, &iova, GFP_KERNEL); virt = dma_alloc_coherent(vic->dev, size, &iova, GFP_KERNEL);
if (!virt) if (!virt) {
return -ENOMEM; err = -ENOMEM;
goto unlock;
}
} else { } else {
virt = tegra_drm_alloc(tegra, size, &iova); virt = tegra_drm_alloc(tegra, size, &iova);
if (IS_ERR(virt)) if (IS_ERR(virt)) {
return PTR_ERR(virt); err = PTR_ERR(virt);
goto unlock;
}
} }
vic->falcon.firmware.virt = virt; vic->falcon.firmware.virt = virt;
...@@ -277,7 +289,28 @@ static int vic_load_firmware(struct vic *vic) ...@@ -277,7 +289,28 @@ static int vic_load_firmware(struct vic *vic)
vic->falcon.firmware.phys = phys; vic->falcon.firmware.phys = phys;
} }
return 0; /*
* Check if firmware is new enough to not require mapping firmware
* to data buffer domains.
*/
fce_bin_data_offset = *(u32 *)(virt + VIC_UCODE_FCE_DATA_OFFSET);
if (!vic->config->supports_sid) {
vic->can_use_context = false;
} else if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
/*
* Firmware will access FCE through STREAMID0, so context
* isolation cannot be used.
*/
vic->can_use_context = false;
dev_warn_once(vic->dev, "context isolation disabled due to old firmware\n");
} else {
vic->can_use_context = true;
}
unlock:
mutex_unlock(&lock);
return err;
cleanup: cleanup:
if (!client->group) if (!client->group)
...@@ -285,11 +318,12 @@ static int vic_load_firmware(struct vic *vic) ...@@ -285,11 +318,12 @@ static int vic_load_firmware(struct vic *vic)
else else
tegra_drm_free(tegra, size, virt, iova); tegra_drm_free(tegra, size, virt, iova);
mutex_unlock(&lock);
return err; return err;
} }
static int vic_runtime_resume(struct device *dev) static int __maybe_unused vic_runtime_resume(struct device *dev)
{ {
struct vic *vic = dev_get_drvdata(dev); struct vic *vic = dev_get_drvdata(dev);
int err; int err;
...@@ -323,7 +357,7 @@ static int vic_runtime_resume(struct device *dev) ...@@ -323,7 +357,7 @@ static int vic_runtime_resume(struct device *dev)
return err; return err;
} }
static int vic_runtime_suspend(struct device *dev) static int __maybe_unused vic_runtime_suspend(struct device *dev)
{ {
struct vic *vic = dev_get_drvdata(dev); struct vic *vic = dev_get_drvdata(dev);
int err; int err;
...@@ -358,10 +392,27 @@ static void vic_close_channel(struct tegra_drm_context *context) ...@@ -358,10 +392,27 @@ static void vic_close_channel(struct tegra_drm_context *context)
host1x_channel_put(context->channel); host1x_channel_put(context->channel);
} }
static int vic_can_use_memory_ctx(struct tegra_drm_client *client, bool *supported)
{
struct vic *vic = to_vic(client);
int err;
/* This doesn't access HW so it's safe to call without powering up. */
err = vic_load_firmware(vic);
if (err < 0)
return err;
*supported = vic->can_use_context;
return 0;
}
static const struct tegra_drm_client_ops vic_ops = { static const struct tegra_drm_client_ops vic_ops = {
.open_channel = vic_open_channel, .open_channel = vic_open_channel,
.close_channel = vic_close_channel, .close_channel = vic_close_channel,
.submit = tegra_drm_submit, .submit = tegra_drm_submit,
.get_streamid_offset = tegra_drm_get_streamid_offset_thi,
.can_use_memory_ctx = vic_can_use_memory_ctx,
}; };
#define NVIDIA_TEGRA_124_VIC_FIRMWARE "nvidia/tegra124/vic03_ucode.bin" #define NVIDIA_TEGRA_124_VIC_FIRMWARE "nvidia/tegra124/vic03_ucode.bin"
...@@ -396,11 +447,20 @@ static const struct vic_config vic_t194_config = { ...@@ -396,11 +447,20 @@ static const struct vic_config vic_t194_config = {
.supports_sid = true, .supports_sid = true,
}; };
#define NVIDIA_TEGRA_234_VIC_FIRMWARE "nvidia/tegra234/vic.bin"
static const struct vic_config vic_t234_config = {
.firmware = NVIDIA_TEGRA_234_VIC_FIRMWARE,
.version = 0x23,
.supports_sid = true,
};
static const struct of_device_id tegra_vic_of_match[] = { static const struct of_device_id tegra_vic_of_match[] = {
{ .compatible = "nvidia,tegra124-vic", .data = &vic_t124_config }, { .compatible = "nvidia,tegra124-vic", .data = &vic_t124_config },
{ .compatible = "nvidia,tegra210-vic", .data = &vic_t210_config }, { .compatible = "nvidia,tegra210-vic", .data = &vic_t210_config },
{ .compatible = "nvidia,tegra186-vic", .data = &vic_t186_config }, { .compatible = "nvidia,tegra186-vic", .data = &vic_t186_config },
{ .compatible = "nvidia,tegra194-vic", .data = &vic_t194_config }, { .compatible = "nvidia,tegra194-vic", .data = &vic_t194_config },
{ .compatible = "nvidia,tegra234-vic", .data = &vic_t234_config },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, tegra_vic_of_match); MODULE_DEVICE_TABLE(of, tegra_vic_of_match);
...@@ -409,7 +469,6 @@ static int vic_probe(struct platform_device *pdev) ...@@ -409,7 +469,6 @@ static int vic_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct host1x_syncpt **syncpts; struct host1x_syncpt **syncpts;
struct resource *regs;
struct vic *vic; struct vic *vic;
int err; int err;
...@@ -430,13 +489,7 @@ static int vic_probe(struct platform_device *pdev) ...@@ -430,13 +489,7 @@ static int vic_probe(struct platform_device *pdev)
if (!syncpts) if (!syncpts)
return -ENOMEM; return -ENOMEM;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); vic->regs = devm_platform_ioremap_resource(pdev, 0);
if (!regs) {
dev_err(&pdev->dev, "failed to get registers\n");
return -ENXIO;
}
vic->regs = devm_ioremap_resource(dev, regs);
if (IS_ERR(vic->regs)) if (IS_ERR(vic->regs))
return PTR_ERR(vic->regs); return PTR_ERR(vic->regs);
...@@ -539,3 +592,6 @@ MODULE_FIRMWARE(NVIDIA_TEGRA_186_VIC_FIRMWARE); ...@@ -539,3 +592,6 @@ MODULE_FIRMWARE(NVIDIA_TEGRA_186_VIC_FIRMWARE);
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) #if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC)
MODULE_FIRMWARE(NVIDIA_TEGRA_194_VIC_FIRMWARE); MODULE_FIRMWARE(NVIDIA_TEGRA_194_VIC_FIRMWARE);
#endif #endif
#if IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
MODULE_FIRMWARE(NVIDIA_TEGRA_234_VIC_FIRMWARE);
#endif
...@@ -15,7 +15,11 @@ host1x-y = \ ...@@ -15,7 +15,11 @@ host1x-y = \
hw/host1x04.o \ hw/host1x04.o \
hw/host1x05.o \ hw/host1x05.o \
hw/host1x06.o \ hw/host1x06.o \
hw/host1x07.o hw/host1x07.o \
hw/host1x08.o
host1x-$(CONFIG_IOMMU_API) += \
context.o
obj-$(CONFIG_TEGRA_HOST1X) += host1x.o obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
obj-$(CONFIG_TEGRA_HOST1X_CONTEXT_BUS) += context_bus.o obj-$(CONFIG_TEGRA_HOST1X_CONTEXT_BUS) += context_bus.o
...@@ -457,10 +457,25 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma, ...@@ -457,10 +457,25 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
* to offset 0xbad. This does nothing but * to offset 0xbad. This does nothing but
* has a easily detected signature in debug * has a easily detected signature in debug
* traces. * traces.
*
* On systems with MLOCK enforcement enabled,
* the above 0 word writes would fall foul of
* the enforcement. As such, in the first slot
* put a RESTART_W opcode to the beginning
* of the next job. We don't use this for older
* chips since those only support the RESTART
* opcode with inconvenient alignment requirements.
*/ */
if (i == 0 && host1x->info->has_wide_gather) {
unsigned int next_job = (job->first_get/8 + job->num_slots)
% HOST1X_PUSHBUFFER_SLOTS;
mapped[2*slot+0] = (0xd << 28) | (next_job * 2);
mapped[2*slot+1] = 0x0;
} else {
mapped[2*slot+0] = 0x1bad0000; mapped[2*slot+0] = 0x1bad0000;
mapped[2*slot+1] = 0x1bad0000; mapped[2*slot+1] = 0x1bad0000;
} }
}
job->cancelled = true; job->cancelled = true;
} }
...@@ -600,8 +615,8 @@ void host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2, ...@@ -600,8 +615,8 @@ void host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2,
struct host1x_channel *channel = cdma_to_channel(cdma); struct host1x_channel *channel = cdma_to_channel(cdma);
struct host1x *host1x = cdma_to_host1x(cdma); struct host1x *host1x = cdma_to_host1x(cdma);
struct push_buffer *pb = &cdma->push_buffer; struct push_buffer *pb = &cdma->push_buffer;
unsigned int needed = 2, extra = 0, i;
unsigned int space = cdma->slots_free; unsigned int space = cdma->slots_free;
unsigned int needed = 2, extra = 0;
if (host1x_debug_trace_cmdbuf) if (host1x_debug_trace_cmdbuf)
trace_host1x_cdma_push_wide(dev_name(channel->dev), op1, op2, trace_host1x_cdma_push_wide(dev_name(channel->dev), op1, op2,
...@@ -619,20 +634,14 @@ void host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2, ...@@ -619,20 +634,14 @@ void host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2,
cdma->slots_free = space - needed; cdma->slots_free = space - needed;
cdma->slots_used += needed; cdma->slots_used += needed;
if (extra > 0) {
/* /*
* Note that we rely on the fact that this is only used to submit wide * If there isn't enough space at the tail of the pushbuffer,
* gather opcodes, which consist of 3 words, and they are padded with * insert a RESTART(0) here to go back to the beginning.
* a NOP to avoid having to deal with fractional slots (a slot always * The code above adjusted the indexes appropriately.
* represents 2 words). The fourth opcode passed to this function will
* therefore always be a NOP.
*
* This works around a slight ambiguity when it comes to opcodes. For
* all current host1x incarnations the NOP opcode uses the exact same
* encoding (0x20000000), so we could hard-code the value here, but a
* new incarnation may change it and break that assumption.
*/ */
for (i = 0; i < extra; i++) host1x_pushbuffer_push(pb, (0x5 << 28), 0xdead0000);
host1x_pushbuffer_push(pb, op4, op4); }
host1x_pushbuffer_push(pb, op1, op2); host1x_pushbuffer_push(pb, op1, op2);
host1x_pushbuffer_push(pb, op3, op4); host1x_pushbuffer_push(pb, op3, op4);
......
...@@ -21,22 +21,18 @@ int host1x_channel_list_init(struct host1x_channel_list *chlist, ...@@ -21,22 +21,18 @@ int host1x_channel_list_init(struct host1x_channel_list *chlist,
if (!chlist->channels) if (!chlist->channels)
return -ENOMEM; return -ENOMEM;
chlist->allocated_channels = chlist->allocated_channels = bitmap_zalloc(num_channels, GFP_KERNEL);
kcalloc(BITS_TO_LONGS(num_channels), sizeof(unsigned long),
GFP_KERNEL);
if (!chlist->allocated_channels) { if (!chlist->allocated_channels) {
kfree(chlist->channels); kfree(chlist->channels);
return -ENOMEM; return -ENOMEM;
} }
bitmap_zero(chlist->allocated_channels, num_channels);
return 0; return 0;
} }
void host1x_channel_list_free(struct host1x_channel_list *chlist) void host1x_channel_list_free(struct host1x_channel_list *chlist)
{ {
kfree(chlist->allocated_channels); bitmap_free(chlist->allocated_channels);
kfree(chlist->channels); kfree(chlist->channels);
} }
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, NVIDIA Corporation.
*/
#include <linux/device.h>
#include <linux/kref.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pid.h>
#include <linux/slab.h>
#include "context.h"
#include "dev.h"
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;
int err;
cdl->devs = NULL;
cdl->len = 0;
mutex_init(&cdl->lock);
err = of_property_count_u32_elems(node, "iommu-map");
if (err < 0)
return 0;
cdl->devs = kcalloc(err, sizeof(*cdl->devs), GFP_KERNEL);
if (!cdl->devs)
return -ENOMEM;
cdl->len = err / 4;
for (i = 0; i < cdl->len; i++) {
struct iommu_fwspec *fwspec;
ctx = &cdl->devs[i];
ctx->host = host1x;
device_initialize(&ctx->dev);
/*
* Due to an issue with T194 NVENC, only 38 bits can be used.
* Anyway, 256GiB of IOVA ought to be enough for anyone.
*/
ctx->dma_mask = DMA_BIT_MASK(38);
ctx->dev.dma_mask = &ctx->dma_mask;
ctx->dev.coherent_dma_mask = ctx->dma_mask;
dev_set_name(&ctx->dev, "host1x-ctx.%d", i);
ctx->dev.bus = &host1x_context_device_bus_type;
ctx->dev.parent = host1x->dev;
dma_set_max_seg_size(&ctx->dev, UINT_MAX);
err = device_add(&ctx->dev);
if (err) {
dev_err(host1x->dev, "could not add context device %d: %d\n", i, err);
goto del_devices;
}
err = of_dma_configure_id(&ctx->dev, node, true, &i);
if (err) {
dev_err(host1x->dev, "IOMMU configuration failed for context device %d: %d\n",
i, err);
device_del(&ctx->dev);
goto del_devices;
}
fwspec = dev_iommu_fwspec_get(&ctx->dev);
if (!fwspec || !device_iommu_mapped(&ctx->dev)) {
dev_err(host1x->dev, "Context device %d has no IOMMU!\n", i);
device_del(&ctx->dev);
goto del_devices;
}
ctx->stream_id = fwspec->ids[0] & 0xffff;
}
return 0;
del_devices:
while (i--)
device_del(&cdl->devs[i].dev);
kfree(cdl->devs);
cdl->len = 0;
return err;
}
void host1x_memory_context_list_free(struct host1x_memory_context_list *cdl)
{
unsigned int i;
for (i = 0; i < cdl->len; i++)
device_del(&cdl->devs[i].dev);
kfree(cdl->devs);
cdl->len = 0;
}
struct host1x_memory_context *host1x_memory_context_alloc(struct host1x *host1x,
struct pid *pid)
{
struct host1x_memory_context_list *cdl = &host1x->context_list;
struct host1x_memory_context *free = NULL;
int i;
if (!cdl->len)
return ERR_PTR(-EOPNOTSUPP);
mutex_lock(&cdl->lock);
for (i = 0; i < cdl->len; i++) {
struct host1x_memory_context *cd = &cdl->devs[i];
if (cd->owner == pid) {
refcount_inc(&cd->ref);
mutex_unlock(&cdl->lock);
return cd;
} else if (!cd->owner && !free) {
free = cd;
}
}
if (!free) {
mutex_unlock(&cdl->lock);
return ERR_PTR(-EBUSY);
}
refcount_set(&free->ref, 1);
free->owner = get_pid(pid);
mutex_unlock(&cdl->lock);
return free;
}
EXPORT_SYMBOL_GPL(host1x_memory_context_alloc);
void host1x_memory_context_get(struct host1x_memory_context *cd)
{
refcount_inc(&cd->ref);
}
EXPORT_SYMBOL_GPL(host1x_memory_context_get);
void host1x_memory_context_put(struct host1x_memory_context *cd)
{
struct host1x_memory_context_list *cdl = &cd->host->context_list;
if (refcount_dec_and_mutex_lock(&cd->ref, &cdl->lock)) {
put_pid(cd->owner);
cd->owner = NULL;
mutex_unlock(&cdl->lock);
}
}
EXPORT_SYMBOL_GPL(host1x_memory_context_put);
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Host1x context devices
*
* Copyright (c) 2020, NVIDIA Corporation.
*/
#ifndef __HOST1X_CONTEXT_H
#define __HOST1X_CONTEXT_H
#include <linux/mutex.h>
#include <linux/refcount.h>
struct host1x;
extern struct bus_type host1x_context_device_bus_type;
struct host1x_memory_context_list {
struct mutex lock;
struct host1x_memory_context *devs;
unsigned int len;
};
#ifdef CONFIG_IOMMU_API
int host1x_memory_context_list_init(struct host1x *host1x);
void host1x_memory_context_list_free(struct host1x_memory_context_list *cdl);
#else
static inline int host1x_memory_context_list_init(struct host1x *host1x)
{
return 0;
}
static inline void host1x_memory_context_list_free(struct host1x_memory_context_list *cdl)
{
}
#endif
#endif
...@@ -15,11 +15,6 @@ static int __init host1x_context_device_bus_init(void) ...@@ -15,11 +15,6 @@ static int __init host1x_context_device_bus_init(void)
{ {
int err; int err;
if (!of_machine_is_compatible("nvidia,tegra186") &&
!of_machine_is_compatible("nvidia,tegra194") &&
!of_machine_is_compatible("nvidia,tegra234"))
return 0;
err = bus_register(&host1x_context_device_bus_type); err = bus_register(&host1x_context_device_bus_type);
if (err < 0) { if (err < 0) {
pr_err("bus type registration failed: %d\n", err); pr_err("bus type registration failed: %d\n", err);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "bus.h" #include "bus.h"
#include "channel.h" #include "channel.h"
#include "context.h"
#include "debug.h" #include "debug.h"
#include "dev.h" #include "dev.h"
#include "intr.h" #include "intr.h"
...@@ -38,6 +39,12 @@ ...@@ -38,6 +39,12 @@
#include "hw/host1x05.h" #include "hw/host1x05.h"
#include "hw/host1x06.h" #include "hw/host1x06.h"
#include "hw/host1x07.h" #include "hw/host1x07.h"
#include "hw/host1x08.h"
void host1x_common_writel(struct host1x *host1x, u32 v, u32 r)
{
writel(v, host1x->common_regs + r);
}
void host1x_hypervisor_writel(struct host1x *host1x, u32 v, u32 r) void host1x_hypervisor_writel(struct host1x *host1x, u32 v, u32 r)
{ {
...@@ -199,7 +206,48 @@ static const struct host1x_info host1x07_info = { ...@@ -199,7 +206,48 @@ static const struct host1x_info host1x07_info = {
.reserve_vblank_syncpts = false, .reserve_vblank_syncpts = false,
}; };
/*
* Tegra234 has two stream ID protection tables, one for setting stream IDs
* through the channel path via SETSTREAMID, and one for setting them via
* MMIO. We program each engine's data stream ID in the channel path table
* and firmware stream ID in the MMIO path table.
*/
static const struct host1x_sid_entry tegra234_sid_table[] = {
{
/* VIC channel */
.base = 0x17b8,
.offset = 0x30,
.limit = 0x30
},
{
/* VIC MMIO */
.base = 0x1688,
.offset = 0x34,
.limit = 0x34
},
};
static const struct host1x_info host1x08_info = {
.nb_channels = 63,
.nb_pts = 1024,
.nb_mlocks = 24,
.nb_bases = 0,
.init = host1x08_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(tegra234_sid_table),
.sid_table = tegra234_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[] = { static const struct of_device_id host1x_of_match[] = {
{ .compatible = "nvidia,tegra234-host1x", .data = &host1x08_info, },
{ .compatible = "nvidia,tegra194-host1x", .data = &host1x07_info, }, { .compatible = "nvidia,tegra194-host1x", .data = &host1x07_info, },
{ .compatible = "nvidia,tegra186-host1x", .data = &host1x06_info, }, { .compatible = "nvidia,tegra186-host1x", .data = &host1x06_info, },
{ .compatible = "nvidia,tegra210-host1x", .data = &host1x05_info, }, { .compatible = "nvidia,tegra210-host1x", .data = &host1x05_info, },
...@@ -211,7 +259,7 @@ static const struct of_device_id host1x_of_match[] = { ...@@ -211,7 +259,7 @@ static const struct of_device_id host1x_of_match[] = {
}; };
MODULE_DEVICE_TABLE(of, host1x_of_match); MODULE_DEVICE_TABLE(of, host1x_of_match);
static void host1x_setup_sid_table(struct host1x *host) static void host1x_setup_virtualization_tables(struct host1x *host)
{ {
const struct host1x_info *info = host->info; const struct host1x_info *info = host->info;
unsigned int i; unsigned int i;
...@@ -225,6 +273,21 @@ static void host1x_setup_sid_table(struct host1x *host) ...@@ -225,6 +273,21 @@ static void host1x_setup_sid_table(struct host1x *host)
host1x_hypervisor_writel(host, entry->offset, entry->base); host1x_hypervisor_writel(host, entry->offset, entry->base);
host1x_hypervisor_writel(host, entry->limit, entry->base + 4); host1x_hypervisor_writel(host, entry->limit, entry->base + 4);
} }
for (i = 0; i < info->streamid_vm_table.count; i++) {
/* Allow access to all stream IDs to all VMs. */
host1x_hypervisor_writel(host, 0xff, info->streamid_vm_table.base + 4 * i);
}
for (i = 0; i < info->classid_vm_table.count; i++) {
/* Allow access to all classes to all VMs. */
host1x_hypervisor_writel(host, 0xff, info->classid_vm_table.base + 4 * i);
}
for (i = 0; i < info->mmio_vm_table.count; i++) {
/* Use VM1 (that's us) as originator VMID for engine MMIO accesses. */
host1x_hypervisor_writel(host, 0x1, info->mmio_vm_table.base + 4 * i);
}
} }
static bool host1x_wants_iommu(struct host1x *host1x) static bool host1x_wants_iommu(struct host1x *host1x)
...@@ -402,16 +465,12 @@ static int host1x_get_resets(struct host1x *host) ...@@ -402,16 +465,12 @@ static int host1x_get_resets(struct host1x *host)
return err; return err;
} }
if (WARN_ON(!host->resets[1].rstc))
return -ENOENT;
return 0; return 0;
} }
static int host1x_probe(struct platform_device *pdev) static int host1x_probe(struct platform_device *pdev)
{ {
struct host1x *host; struct host1x *host;
struct resource *regs, *hv_regs = NULL;
int syncpt_irq; int syncpt_irq;
int err; int err;
...@@ -422,25 +481,23 @@ static int host1x_probe(struct platform_device *pdev) ...@@ -422,25 +481,23 @@ static int host1x_probe(struct platform_device *pdev)
host->info = of_device_get_match_data(&pdev->dev); host->info = of_device_get_match_data(&pdev->dev);
if (host->info->has_hypervisor) { if (host->info->has_hypervisor) {
regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vm"); host->regs = devm_platform_ioremap_resource_byname(pdev, "vm");
if (!regs) { if (IS_ERR(host->regs))
dev_err(&pdev->dev, "failed to get vm registers\n"); return PTR_ERR(host->regs);
return -ENXIO;
}
hv_regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, host->hv_regs = devm_platform_ioremap_resource_byname(pdev, "hypervisor");
"hypervisor"); if (IS_ERR(host->hv_regs))
if (!hv_regs) { return PTR_ERR(host->hv_regs);
dev_err(&pdev->dev,
"failed to get hypervisor registers\n"); if (host->info->has_common) {
return -ENXIO; host->common_regs = devm_platform_ioremap_resource_byname(pdev, "common");
if (IS_ERR(host->common_regs))
return PTR_ERR(host->common_regs);
} }
} else { } else {
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->regs = devm_platform_ioremap_resource(pdev, 0);
if (!regs) { if (IS_ERR(host->regs))
dev_err(&pdev->dev, "failed to get registers\n"); return PTR_ERR(host->regs);
return -ENXIO;
}
} }
syncpt_irq = platform_get_irq(pdev, 0); syncpt_irq = platform_get_irq(pdev, 0);
...@@ -455,16 +512,6 @@ static int host1x_probe(struct platform_device *pdev) ...@@ -455,16 +512,6 @@ static int host1x_probe(struct platform_device *pdev)
/* set common host1x device data */ /* set common host1x device data */
platform_set_drvdata(pdev, host); platform_set_drvdata(pdev, host);
host->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(host->regs))
return PTR_ERR(host->regs);
if (host->info->has_hypervisor) {
host->hv_regs = devm_ioremap_resource(&pdev->dev, hv_regs);
if (IS_ERR(host->hv_regs))
return PTR_ERR(host->hv_regs);
}
host->dev->dma_parms = &host->dma_parms; host->dev->dma_parms = &host->dma_parms;
dma_set_max_seg_size(host->dev, UINT_MAX); dma_set_max_seg_size(host->dev, UINT_MAX);
...@@ -503,10 +550,16 @@ static int host1x_probe(struct platform_device *pdev) ...@@ -503,10 +550,16 @@ static int host1x_probe(struct platform_device *pdev)
goto iommu_exit; goto iommu_exit;
} }
err = host1x_memory_context_list_init(host);
if (err) {
dev_err(&pdev->dev, "failed to initialize context list\n");
goto free_channels;
}
err = host1x_syncpt_init(host); err = host1x_syncpt_init(host);
if (err) { if (err) {
dev_err(&pdev->dev, "failed to initialize syncpts\n"); dev_err(&pdev->dev, "failed to initialize syncpts\n");
goto free_channels; goto free_contexts;
} }
err = host1x_intr_init(host, syncpt_irq); err = host1x_intr_init(host, syncpt_irq);
...@@ -550,6 +603,8 @@ static int host1x_probe(struct platform_device *pdev) ...@@ -550,6 +603,8 @@ static int host1x_probe(struct platform_device *pdev)
host1x_intr_deinit(host); host1x_intr_deinit(host);
deinit_syncpt: deinit_syncpt:
host1x_syncpt_deinit(host); host1x_syncpt_deinit(host);
free_contexts:
host1x_memory_context_list_free(&host->context_list);
free_channels: free_channels:
host1x_channel_list_free(&host->channel_list); host1x_channel_list_free(&host->channel_list);
iommu_exit: iommu_exit:
...@@ -571,6 +626,7 @@ static int host1x_remove(struct platform_device *pdev) ...@@ -571,6 +626,7 @@ static int host1x_remove(struct platform_device *pdev)
host1x_intr_deinit(host); host1x_intr_deinit(host);
host1x_syncpt_deinit(host); host1x_syncpt_deinit(host);
host1x_memory_context_list_free(&host->context_list);
host1x_channel_list_free(&host->channel_list); host1x_channel_list_free(&host->channel_list);
host1x_iommu_exit(host); host1x_iommu_exit(host);
host1x_bo_cache_destroy(&host->cache); host1x_bo_cache_destroy(&host->cache);
...@@ -600,7 +656,7 @@ static int __maybe_unused host1x_runtime_suspend(struct device *dev) ...@@ -600,7 +656,7 @@ static int __maybe_unused host1x_runtime_suspend(struct device *dev)
return 0; return 0;
resume_host1x: resume_host1x:
host1x_setup_sid_table(host); host1x_setup_virtualization_tables(host);
host1x_syncpt_restore(host); host1x_syncpt_restore(host);
host1x_intr_start(host); host1x_intr_start(host);
...@@ -630,7 +686,7 @@ static int __maybe_unused host1x_runtime_resume(struct device *dev) ...@@ -630,7 +686,7 @@ static int __maybe_unused host1x_runtime_resume(struct device *dev)
goto disable_clk; goto disable_clk;
} }
host1x_setup_sid_table(host); host1x_setup_virtualization_tables(host);
host1x_syncpt_restore(host); host1x_syncpt_restore(host);
host1x_intr_start(host); host1x_intr_start(host);
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "cdma.h" #include "cdma.h"
#include "channel.h" #include "channel.h"
#include "context.h"
#include "intr.h" #include "intr.h"
#include "job.h" #include "job.h"
#include "syncpt.h" #include "syncpt.h"
...@@ -89,6 +90,11 @@ struct host1x_sid_entry { ...@@ -89,6 +90,11 @@ struct host1x_sid_entry {
unsigned int limit; unsigned int limit;
}; };
struct host1x_table_desc {
unsigned int base;
unsigned int count;
};
struct host1x_info { struct host1x_info {
unsigned int nb_channels; /* host1x: number of channels supported */ unsigned int nb_channels; /* host1x: number of channels supported */
unsigned int nb_pts; /* host1x: number of syncpoints supported */ unsigned int nb_pts; /* host1x: number of syncpoints supported */
...@@ -99,8 +105,12 @@ struct host1x_info { ...@@ -99,8 +105,12 @@ struct host1x_info {
u64 dma_mask; /* mask of addressable memory */ u64 dma_mask; /* mask of addressable memory */
bool has_wide_gather; /* supports GATHER_W opcode */ bool has_wide_gather; /* supports GATHER_W opcode */
bool has_hypervisor; /* has hypervisor registers */ bool has_hypervisor; /* has hypervisor registers */
bool has_common; /* has common registers separate from hypervisor */
unsigned int num_sid_entries; unsigned int num_sid_entries;
const struct host1x_sid_entry *sid_table; const struct host1x_sid_entry *sid_table;
struct host1x_table_desc streamid_vm_table;
struct host1x_table_desc classid_vm_table;
struct host1x_table_desc mmio_vm_table;
/* /*
* On T20-T148, the boot chain may setup DC to increment syncpoints * On T20-T148, the boot chain may setup DC to increment syncpoints
* 26/27 on VBLANK. As such we cannot use these syncpoints until * 26/27 on VBLANK. As such we cannot use these syncpoints until
...@@ -114,6 +124,7 @@ struct host1x { ...@@ -114,6 +124,7 @@ struct host1x {
void __iomem *regs; void __iomem *regs;
void __iomem *hv_regs; /* hypervisor region */ void __iomem *hv_regs; /* hypervisor region */
void __iomem *common_regs;
struct host1x_syncpt *syncpt; struct host1x_syncpt *syncpt;
struct host1x_syncpt_base *bases; struct host1x_syncpt_base *bases;
struct device *dev; struct device *dev;
...@@ -141,6 +152,7 @@ struct host1x { ...@@ -141,6 +152,7 @@ struct host1x {
struct mutex syncpt_mutex; struct mutex syncpt_mutex;
struct host1x_channel_list channel_list; struct host1x_channel_list channel_list;
struct host1x_memory_context_list context_list;
struct dentry *debugfs; struct dentry *debugfs;
...@@ -154,6 +166,7 @@ struct host1x { ...@@ -154,6 +166,7 @@ struct host1x {
struct host1x_bo_cache cache; struct host1x_bo_cache cache;
}; };
void host1x_common_writel(struct host1x *host1x, u32 v, u32 r);
void host1x_hypervisor_writel(struct host1x *host1x, u32 r, u32 v); void host1x_hypervisor_writel(struct host1x *host1x, u32 r, u32 v);
u32 host1x_hypervisor_readl(struct host1x *host1x, u32 r); u32 host1x_hypervisor_readl(struct host1x *host1x, u32 r);
void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v); void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v);
......
...@@ -238,6 +238,37 @@ static void cdma_resume(struct host1x_cdma *cdma, u32 getptr) ...@@ -238,6 +238,37 @@ static void cdma_resume(struct host1x_cdma *cdma, u32 getptr)
cdma_timeout_restart(cdma, getptr); cdma_timeout_restart(cdma, getptr);
} }
static void timeout_release_mlock(struct host1x_cdma *cdma)
{
#if HOST1X_HW >= 8
/* Tegra186 and Tegra194 require a more complicated MLOCK release
* sequence. Furthermore, those chips by default don't enforce MLOCKs,
* 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.
*/
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;
case HOST1X_CLASS_NVDEC:
offset = HOST1X_COMMON_NVDEC_MLOCK;
break;
default:
WARN(1, "%s was not updated for class %u", __func__, ch->client->class);
return;
}
host1x_common_writel(host1x, 0x0, offset);
#endif
}
/* /*
* If this timeout fires, it indicates the current sync_queue entry has * If this timeout fires, it indicates the current sync_queue entry has
* exceeded its TTL and the userctx should be timed out and remaining * exceeded its TTL and the userctx should be timed out and remaining
...@@ -288,6 +319,9 @@ static void cdma_timeout_handler(struct work_struct *work) ...@@ -288,6 +319,9 @@ static void cdma_timeout_handler(struct work_struct *work)
/* stop HW, resetting channel/module */ /* stop HW, resetting channel/module */
host1x_hw_cdma_freeze(host1x, cdma); host1x_hw_cdma_freeze(host1x, cdma);
/* release any held MLOCK */
timeout_release_mlock(cdma);
host1x_cdma_update_sync_queue(cdma, ch->dev); host1x_cdma_update_sync_queue(cdma, ch->dev);
mutex_unlock(&cdma->lock); mutex_unlock(&cdma->lock);
} }
......
...@@ -47,10 +47,41 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo, ...@@ -47,10 +47,41 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo,
} }
} }
static void submit_wait(struct host1x_cdma *cdma, u32 id, u32 threshold, static void submit_wait(struct host1x_job *job, u32 id, u32 threshold,
u32 next_class) u32 next_class)
{ {
#if HOST1X_HW >= 2 struct host1x_cdma *cdma = &job->channel->cdma;
#if HOST1X_HW >= 6
u32 stream_id;
/*
* If a memory context has been set, use it. Otherwise
* (if context isolation is disabled) use the engine's
* firmware stream ID.
*/
if (job->memory_context)
stream_id = job->memory_context->stream_id;
else
stream_id = job->engine_fallback_streamid;
host1x_cdma_push_wide(cdma,
host1x_opcode_setclass(
HOST1X_CLASS_HOST1X,
HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32,
/* WAIT_SYNCPT_32 is at SYNCPT_PAYLOAD_32+2 */
BIT(0) | BIT(2)
),
threshold,
id,
HOST1X_OPCODE_NOP
);
host1x_cdma_push_wide(&job->channel->cdma,
host1x_opcode_setclass(job->class, 0, 0),
host1x_opcode_setpayload(stream_id),
host1x_opcode_setstreamid(job->engine_streamid_offset / 4),
HOST1X_OPCODE_NOP);
#elif HOST1X_HW >= 2
host1x_cdma_push_wide(cdma, host1x_cdma_push_wide(cdma,
host1x_opcode_setclass( host1x_opcode_setclass(
HOST1X_CLASS_HOST1X, HOST1X_CLASS_HOST1X,
...@@ -97,7 +128,7 @@ static void submit_gathers(struct host1x_job *job, u32 job_syncpt_base) ...@@ -97,7 +128,7 @@ static void submit_gathers(struct host1x_job *job, u32 job_syncpt_base)
else else
threshold = cmd->wait.threshold; threshold = cmd->wait.threshold;
submit_wait(cdma, cmd->wait.id, threshold, cmd->wait.next_class); submit_wait(job, cmd->wait.id, threshold, cmd->wait.next_class);
} else { } else {
struct host1x_job_gather *g = &cmd->gather; struct host1x_job_gather *g = &cmd->gather;
...@@ -180,11 +211,77 @@ static void host1x_enable_gather_filter(struct host1x_channel *ch) ...@@ -180,11 +211,77 @@ static void host1x_enable_gather_filter(struct host1x_channel *ch)
#endif #endif
} }
static void channel_program_cdma(struct host1x_job *job)
{
struct host1x_cdma *cdma = &job->channel->cdma;
struct host1x_syncpt *sp = job->syncpt;
#if HOST1X_HW >= 6
u32 fence;
/* Enter engine class with invalid stream ID. */
host1x_cdma_push_wide(cdma,
host1x_opcode_acquire_mlock(job->class),
host1x_opcode_setclass(job->class, 0, 0),
host1x_opcode_setpayload(0),
host1x_opcode_setstreamid(job->engine_streamid_offset / 4));
/* Before switching stream ID to real stream ID, ensure engine is idle. */
fence = host1x_syncpt_incr_max(sp, 1);
host1x_cdma_push(&job->channel->cdma,
host1x_opcode_nonincr(HOST1X_UCLASS_INCR_SYNCPT, 1),
HOST1X_UCLASS_INCR_SYNCPT_INDX_F(job->syncpt->id) |
HOST1X_UCLASS_INCR_SYNCPT_COND_F(4));
submit_wait(job, job->syncpt->id, fence, job->class);
/* Submit work. */
job->syncpt_end = host1x_syncpt_incr_max(sp, job->syncpt_incrs);
submit_gathers(job, job->syncpt_end - job->syncpt_incrs);
/* Before releasing MLOCK, ensure engine is idle again. */
fence = host1x_syncpt_incr_max(sp, 1);
host1x_cdma_push(&job->channel->cdma,
host1x_opcode_nonincr(HOST1X_UCLASS_INCR_SYNCPT, 1),
HOST1X_UCLASS_INCR_SYNCPT_INDX_F(job->syncpt->id) |
HOST1X_UCLASS_INCR_SYNCPT_COND_F(4));
submit_wait(job, job->syncpt->id, fence, job->class);
/* Release MLOCK. */
host1x_cdma_push(cdma,
HOST1X_OPCODE_NOP, host1x_opcode_release_mlock(job->class));
#else
if (job->serialize) {
/*
* Force serialization by inserting a host wait for the
* previous job to finish before this one can commence.
*/
host1x_cdma_push(cdma,
host1x_opcode_setclass(HOST1X_CLASS_HOST1X,
host1x_uclass_wait_syncpt_r(), 1),
host1x_class_host_wait_syncpt(job->syncpt->id,
host1x_syncpt_read_max(sp)));
}
/* Synchronize base register to allow using it for relative waiting */
if (sp->base)
synchronize_syncpt_base(job);
/* add a setclass for modules that require it */
if (job->class)
host1x_cdma_push(cdma,
host1x_opcode_setclass(job->class, 0, 0),
HOST1X_OPCODE_NOP);
job->syncpt_end = host1x_syncpt_incr_max(sp, job->syncpt_incrs);
submit_gathers(job, job->syncpt_end - job->syncpt_incrs);
#endif
}
static int channel_submit(struct host1x_job *job) static int channel_submit(struct host1x_job *job)
{ {
struct host1x_channel *ch = job->channel; struct host1x_channel *ch = job->channel;
struct host1x_syncpt *sp = job->syncpt; struct host1x_syncpt *sp = job->syncpt;
u32 user_syncpt_incrs = job->syncpt_incrs;
u32 prev_max = 0; u32 prev_max = 0;
u32 syncval; u32 syncval;
int err; int err;
...@@ -212,6 +309,7 @@ static int channel_submit(struct host1x_job *job) ...@@ -212,6 +309,7 @@ static int channel_submit(struct host1x_job *job)
host1x_channel_set_streamid(ch); host1x_channel_set_streamid(ch);
host1x_enable_gather_filter(ch); host1x_enable_gather_filter(ch);
host1x_hw_syncpt_assign_to_channel(host, sp, ch);
/* begin a CDMA submit */ /* begin a CDMA submit */
err = host1x_cdma_begin(&ch->cdma, job); err = host1x_cdma_begin(&ch->cdma, job);
...@@ -220,35 +318,8 @@ static int channel_submit(struct host1x_job *job) ...@@ -220,35 +318,8 @@ static int channel_submit(struct host1x_job *job)
goto error; goto error;
} }
if (job->serialize) { channel_program_cdma(job);
/* syncval = host1x_syncpt_read_max(sp);
* Force serialization by inserting a host wait for the
* previous job to finish before this one can commence.
*/
host1x_cdma_push(&ch->cdma,
host1x_opcode_setclass(HOST1X_CLASS_HOST1X,
host1x_uclass_wait_syncpt_r(), 1),
host1x_class_host_wait_syncpt(job->syncpt->id,
host1x_syncpt_read_max(sp)));
}
/* Synchronize base register to allow using it for relative waiting */
if (sp->base)
synchronize_syncpt_base(job);
syncval = host1x_syncpt_incr_max(sp, user_syncpt_incrs);
host1x_hw_syncpt_assign_to_channel(host, sp, ch);
job->syncpt_end = syncval;
/* add a setclass for modules that require it */
if (job->class)
host1x_cdma_push(&ch->cdma,
host1x_opcode_setclass(job->class, 0, 0),
HOST1X_OPCODE_NOP);
submit_gathers(job, syncval - user_syncpt_incrs);
/* end CDMA submit & stash pinned hMems into sync queue */ /* end CDMA submit & stash pinned hMems into sync queue */
host1x_cdma_end(&ch->cdma, job); host1x_cdma_end(&ch->cdma, job);
......
...@@ -15,118 +15,6 @@ ...@@ -15,118 +15,6 @@
#include "hw_host1x01_sync.h" #include "hw_host1x01_sync.h"
#include "hw_host1x01_uclass.h" #include "hw_host1x01_uclass.h"
static inline u32 host1x_class_host_wait_syncpt( #include "opcodes.h"
unsigned indx, unsigned threshold)
{
return host1x_uclass_wait_syncpt_indx_f(indx)
| host1x_uclass_wait_syncpt_thresh_f(threshold);
}
static inline u32 host1x_class_host_load_syncpt_base(
unsigned indx, unsigned threshold)
{
return host1x_uclass_load_syncpt_base_base_indx_f(indx)
| host1x_uclass_load_syncpt_base_value_f(threshold);
}
static inline u32 host1x_class_host_wait_syncpt_base(
unsigned indx, unsigned base_indx, unsigned offset)
{
return host1x_uclass_wait_syncpt_base_indx_f(indx)
| host1x_uclass_wait_syncpt_base_base_indx_f(base_indx)
| host1x_uclass_wait_syncpt_base_offset_f(offset);
}
static inline u32 host1x_class_host_incr_syncpt_base(
unsigned base_indx, unsigned offset)
{
return host1x_uclass_incr_syncpt_base_base_indx_f(base_indx)
| host1x_uclass_incr_syncpt_base_offset_f(offset);
}
static inline u32 host1x_class_host_incr_syncpt(
unsigned cond, unsigned indx)
{
return host1x_uclass_incr_syncpt_cond_f(cond)
| host1x_uclass_incr_syncpt_indx_f(indx);
}
static inline u32 host1x_class_host_indoff_reg_write(
unsigned mod_id, unsigned offset, bool auto_inc)
{
u32 v = host1x_uclass_indoff_indbe_f(0xf)
| host1x_uclass_indoff_indmodid_f(mod_id)
| host1x_uclass_indoff_indroffset_f(offset);
if (auto_inc)
v |= host1x_uclass_indoff_autoinc_f(1);
return v;
}
static inline u32 host1x_class_host_indoff_reg_read(
unsigned mod_id, unsigned offset, bool auto_inc)
{
u32 v = host1x_uclass_indoff_indmodid_f(mod_id)
| host1x_uclass_indoff_indroffset_f(offset)
| host1x_uclass_indoff_rwn_read_v();
if (auto_inc)
v |= host1x_uclass_indoff_autoinc_f(1);
return v;
}
/* cdma opcodes */
static inline u32 host1x_opcode_setclass(
unsigned class_id, unsigned offset, unsigned mask)
{
return (0 << 28) | (offset << 16) | (class_id << 6) | mask;
}
static inline u32 host1x_opcode_incr(unsigned offset, unsigned count)
{
return (1 << 28) | (offset << 16) | count;
}
static inline u32 host1x_opcode_nonincr(unsigned offset, unsigned count)
{
return (2 << 28) | (offset << 16) | count;
}
static inline u32 host1x_opcode_mask(unsigned offset, unsigned mask)
{
return (3 << 28) | (offset << 16) | mask;
}
static inline u32 host1x_opcode_imm(unsigned offset, unsigned value)
{
return (4 << 28) | (offset << 16) | value;
}
static inline u32 host1x_opcode_imm_incr_syncpt(unsigned cond, unsigned indx)
{
return host1x_opcode_imm(host1x_uclass_incr_syncpt_r(),
host1x_class_host_incr_syncpt(cond, indx));
}
static inline u32 host1x_opcode_restart(unsigned address)
{
return (5 << 28) | (address >> 4);
}
static inline u32 host1x_opcode_gather(unsigned count)
{
return (6 << 28) | count;
}
static inline u32 host1x_opcode_gather_nonincr(unsigned offset, unsigned count)
{
return (6 << 28) | (offset << 16) | BIT(15) | count;
}
static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count)
{
return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count;
}
#define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0)
#endif #endif
...@@ -15,117 +15,6 @@ ...@@ -15,117 +15,6 @@
#include "hw_host1x02_sync.h" #include "hw_host1x02_sync.h"
#include "hw_host1x02_uclass.h" #include "hw_host1x02_uclass.h"
static inline u32 host1x_class_host_wait_syncpt( #include "opcodes.h"
unsigned indx, unsigned threshold)
{
return host1x_uclass_wait_syncpt_indx_f(indx)
| host1x_uclass_wait_syncpt_thresh_f(threshold);
}
static inline u32 host1x_class_host_load_syncpt_base(
unsigned indx, unsigned threshold)
{
return host1x_uclass_load_syncpt_base_base_indx_f(indx)
| host1x_uclass_load_syncpt_base_value_f(threshold);
}
static inline u32 host1x_class_host_wait_syncpt_base(
unsigned indx, unsigned base_indx, unsigned offset)
{
return host1x_uclass_wait_syncpt_base_indx_f(indx)
| host1x_uclass_wait_syncpt_base_base_indx_f(base_indx)
| host1x_uclass_wait_syncpt_base_offset_f(offset);
}
static inline u32 host1x_class_host_incr_syncpt_base(
unsigned base_indx, unsigned offset)
{
return host1x_uclass_incr_syncpt_base_base_indx_f(base_indx)
| host1x_uclass_incr_syncpt_base_offset_f(offset);
}
static inline u32 host1x_class_host_incr_syncpt(
unsigned cond, unsigned indx)
{
return host1x_uclass_incr_syncpt_cond_f(cond)
| host1x_uclass_incr_syncpt_indx_f(indx);
}
static inline u32 host1x_class_host_indoff_reg_write(
unsigned mod_id, unsigned offset, bool auto_inc)
{
u32 v = host1x_uclass_indoff_indbe_f(0xf)
| host1x_uclass_indoff_indmodid_f(mod_id)
| host1x_uclass_indoff_indroffset_f(offset);
if (auto_inc)
v |= host1x_uclass_indoff_autoinc_f(1);
return v;
}
static inline u32 host1x_class_host_indoff_reg_read(
unsigned mod_id, unsigned offset, bool auto_inc)
{
u32 v = host1x_uclass_indoff_indmodid_f(mod_id)
| host1x_uclass_indoff_indroffset_f(offset)
| host1x_uclass_indoff_rwn_read_v();
if (auto_inc)
v |= host1x_uclass_indoff_autoinc_f(1);
return v;
}
/* cdma opcodes */
static inline u32 host1x_opcode_setclass(
unsigned class_id, unsigned offset, unsigned mask)
{
return (0 << 28) | (offset << 16) | (class_id << 6) | mask;
}
static inline u32 host1x_opcode_incr(unsigned offset, unsigned count)
{
return (1 << 28) | (offset << 16) | count;
}
static inline u32 host1x_opcode_nonincr(unsigned offset, unsigned count)
{
return (2 << 28) | (offset << 16) | count;
}
static inline u32 host1x_opcode_mask(unsigned offset, unsigned mask)
{
return (3 << 28) | (offset << 16) | mask;
}
static inline u32 host1x_opcode_imm(unsigned offset, unsigned value)
{
return (4 << 28) | (offset << 16) | value;
}
static inline u32 host1x_opcode_imm_incr_syncpt(unsigned cond, unsigned indx)
{
return host1x_opcode_imm(host1x_uclass_incr_syncpt_r(),
host1x_class_host_incr_syncpt(cond, indx));
}
static inline u32 host1x_opcode_restart(unsigned address)
{
return (5 << 28) | (address >> 4);
}
static inline u32 host1x_opcode_gather(unsigned count)
{
return (6 << 28) | count;
}
static inline u32 host1x_opcode_gather_nonincr(unsigned offset, unsigned count)
{
return (6 << 28) | (offset << 16) | BIT(15) | count;
}
static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count)
{
return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count;
}
#define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0)
#endif #endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only
/*
* Host1x init for Tegra234 SoCs
*
* Copyright (c) 2022 NVIDIA Corporation.
*/
/* include hw specification */
#include "host1x08.h"
#include "host1x08_hardware.h"
/* include code */
#define HOST1X_HW 8
#include "cdma_hw.c"
#include "channel_hw.c"
#include "debug_hw.c"
#include "intr_hw.c"
#include "syncpt_hw.c"
#include "../dev.h"
int host1x08_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;
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Host1x init for Tegra234 SoCs
*
* Copyright (c) 2018 NVIDIA Corporation.
*/
#ifndef HOST1X_HOST1X08_H
#define HOST1X_HOST1X08_H
struct host1x;
int host1x08_init(struct host1x *host);
#endif
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Tegra host1x Register Offsets for Tegra234
*
* Copyright (c) 2022 NVIDIA Corporation.
*/
#ifndef __HOST1X_HOST1X08_HARDWARE_H
#define __HOST1X_HOST1X08_HARDWARE_H
#include <linux/types.h>
#include <linux/bitops.h>
#include "hw_host1x08_uclass.h"
#include "hw_host1x08_vm.h"
#include "hw_host1x08_hypervisor.h"
#include "hw_host1x08_common.h"
#include "opcodes.h"
#endif
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 NVIDIA Corporation.
*/
#ifndef HOST1X_HW_HOST1X08_CHANNEL_H
#define HOST1X_HW_HOST1X08_CHANNEL_H
#define HOST1X_CHANNEL_SMMU_STREAMID 0x084
#endif
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 NVIDIA Corporation.
*/
#define HOST1X_COMMON_OFA_MLOCK 0x4050
#define HOST1X_COMMON_NVJPG1_MLOCK 0x4070
#define HOST1X_COMMON_VIC_MLOCK 0x4078
#define HOST1X_COMMON_NVENC_MLOCK 0x407c
#define HOST1X_COMMON_NVDEC_MLOCK 0x4080
#define HOST1X_COMMON_NVJPG_MLOCK 0x4084
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment