Merge tag 'drm-misc-next-2017-03-21' of git://anongit.freedesktop.org/git/drm-misc into drm-next
drm-misc for 4.12, 2nd attempt this week: - topic branch from Jon Corbet for the new graph kerneldoc support - lots of graphs for kms/atomic things using the above - some vblank query tuning from Chris - gem/cma_fops macros - moar docs Driver stuff: - vc4 hdmi audio, yay (Eric) - dw-hdmi polish from a bunch of people - some rockchip dp updates that didn't make last week (Chris Zhong) - misc bridge&driver updates * tag 'drm-misc-next-2017-03-21' of git://anongit.freedesktop.org/git/drm-misc: (37 commits) drm/edid: detect SCDC support in HF-VSDB drm/edid: detect SCDC support in HF-VSDB drm/edid: check for HF-VSDB block drm: Add SCDC helpers drm: bridge: dw-hdmi: add HDMI vendor specific infoframe config drm/bridge: dw_hdmi: support i2c extended read mode drm/msm: add stubs for msm_{perf,rd}_debugfs_cleanup drm: bochs: Don't remove uninitialized fbdev framebuffer drm: vc4: remove redundant check of plane being non-null drm/vc4: use platform_register_drivers dma-fence: add dma_fence_match_context helper drm/vc4: Add HDMI audio support dt-bindings: Document the dmas and dma-names properties for VC4 HDMI drm/atmel-hlcdc: Fix suspend/resume implementation drm: Skip the waitqueue setup for vblank queries drm: Defer disabling the vblank IRQ until the next interrupt (for instant-off) drm/doc: atomic overview, with graph drm/doc: diagram for mode objects and properties drm/doc: Consistent kerneldoc include order drm/doc: Add KMS overview graphs ...
This commit is contained in:
Коммит
edd849e544
|
@ -299,5 +299,5 @@ What: /sys/bus/pci/devices/.../revision
|
|||
Date: November 2016
|
||||
Contact: Emil Velikov <emil.l.velikov@gmail.com>
|
||||
Description:
|
||||
This file contains the revision field of the the PCI device.
|
||||
This file contains the revision field of the PCI device.
|
||||
The value comes from device config space. The file is read only.
|
||||
|
|
|
@ -14,14 +14,17 @@ Contact
|
|||
The Linux kernel security team can be contacted by email at
|
||||
<security@kernel.org>. This is a private list of security officers
|
||||
who will help verify the bug report and develop and release a fix.
|
||||
It is possible that the security team will bring in extra help from
|
||||
area maintainers to understand and fix the security vulnerability.
|
||||
If you already have a fix, please include it with your report, as
|
||||
that can speed up the process considerably. It is possible that the
|
||||
security team will bring in extra help from area maintainers to
|
||||
understand and fix the security vulnerability.
|
||||
|
||||
As it is with any bug, the more information provided the easier it
|
||||
will be to diagnose and fix. Please review the procedure outlined in
|
||||
admin-guide/reporting-bugs.rst if you are unclear about what information is helpful.
|
||||
Any exploit code is very helpful and will not be released without
|
||||
consent from the reporter unless it has already been made public.
|
||||
admin-guide/reporting-bugs.rst if you are unclear about what
|
||||
information is helpful. Any exploit code is very helpful and will not
|
||||
be released without consent from the reporter unless it has already been
|
||||
made public.
|
||||
|
||||
Disclosure
|
||||
----------
|
||||
|
@ -39,6 +42,32 @@ disclosure is from immediate (esp. if it's already publicly known)
|
|||
to a few weeks. As a basic default policy, we expect report date to
|
||||
disclosure date to be on the order of 7 days.
|
||||
|
||||
Coordination
|
||||
------------
|
||||
|
||||
Fixes for sensitive bugs, such as those that might lead to privilege
|
||||
escalations, may need to be coordinated with the private
|
||||
<linux-distros@vs.openwall.org> mailing list so that distribution vendors
|
||||
are well prepared to issue a fixed kernel upon public disclosure of the
|
||||
upstream fix. Distros will need some time to test the proposed patch and
|
||||
will generally request at least a few days of embargo, and vendor update
|
||||
publication prefers to happen Tuesday through Thursday. When appropriate,
|
||||
the security team can assist with this coordination, or the reporter can
|
||||
include linux-distros from the start. In this case, remember to prefix
|
||||
the email Subject line with "[vs]" as described in the linux-distros wiki:
|
||||
<http://oss-security.openwall.org/wiki/mailing-lists/distros#how-to-use-the-lists>
|
||||
|
||||
CVE assignment
|
||||
--------------
|
||||
|
||||
The security team does not normally assign CVEs, nor do we require them
|
||||
for reports or fixes, as this can needlessly complicate the process and
|
||||
may delay the bug handling. If a reporter wishes to have a CVE identifier
|
||||
assigned ahead of public disclosure, they will need to contact the private
|
||||
linux-distros list, described above. When such a CVE identifier is known
|
||||
before a patch is provided, it is desirable to mention it in the commit
|
||||
message, though.
|
||||
|
||||
Non-disclosure agreements
|
||||
-------------------------
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ from load_config import loadConfig
|
|||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include', 'cdomain']
|
||||
extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include', 'cdomain', 'kfigure']
|
||||
|
||||
# The name of the math extension changed on Sphinx 1.4
|
||||
if major == 1 and minor > 3:
|
||||
|
|
|
@ -231,7 +231,7 @@ the reference implementation in drivers/cpufreq/longrun.c
|
|||
Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset.
|
||||
|
||||
get_intermediate should return a stable intermediate frequency platform wants to
|
||||
switch to, and target_intermediate() should set CPU to to that frequency, before
|
||||
switch to, and target_intermediate() should set CPU to that frequency, before
|
||||
jumping to the frequency corresponding to 'index'. Core will take care of
|
||||
sending notifications and driver doesn't have to handle them in
|
||||
target_intermediate() or target_index().
|
||||
|
|
|
@ -34,6 +34,9 @@ Optional properties for HDMI:
|
|||
- hpd-gpios: The GPIO pin for HDMI hotplug detect (if it doesn't appear
|
||||
as an interrupt/status bit in the HDMI controller
|
||||
itself). See bindings/pinctrl/brcm,bcm2835-gpio.txt
|
||||
- dmas: Should contain one entry pointing to the DMA channel used to
|
||||
transfer audio data
|
||||
- dma-names: Should contain "audio-rx"
|
||||
|
||||
Required properties for DPI:
|
||||
- compatible: Should be "brcm,bcm2835-dpi"
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
graph G {
|
||||
Hello -- World
|
||||
}
|
|
@ -34,8 +34,9 @@ format-specific subdirectories under ``Documentation/output``.
|
|||
|
||||
To generate documentation, Sphinx (``sphinx-build``) must obviously be
|
||||
installed. For prettier HTML output, the Read the Docs Sphinx theme
|
||||
(``sphinx_rtd_theme``) is used if available. For PDF output, ``rst2pdf`` is also
|
||||
needed. All of these are widely available and packaged in distributions.
|
||||
(``sphinx_rtd_theme``) is used if available. For PDF output you'll also need
|
||||
``XeLaTeX`` and ``convert(1)`` from ImageMagick (https://www.imagemagick.org).
|
||||
All of these are widely available and packaged in distributions.
|
||||
|
||||
To pass extra options to Sphinx, you can use the ``SPHINXOPTS`` make
|
||||
variable. For example, use ``make SPHINXOPTS=-v htmldocs`` to get more verbose
|
||||
|
@ -73,7 +74,16 @@ Specific guidelines for the kernel documentation
|
|||
|
||||
Here are some specific guidelines for the kernel documentation:
|
||||
|
||||
* Please don't go overboard with reStructuredText markup. Keep it simple.
|
||||
* Please don't go overboard with reStructuredText markup. Keep it
|
||||
simple. For the most part the documentation should be plain text with
|
||||
just enough consistency in formatting that it can be converted to
|
||||
other formats.
|
||||
|
||||
* Please keep the formatting changes minimal when converting existing
|
||||
documentation to reStructuredText.
|
||||
|
||||
* Also update the content, not just the formatting, when converting
|
||||
documentation.
|
||||
|
||||
* Please stick to this order of heading adornments:
|
||||
|
||||
|
@ -103,6 +113,12 @@ Here are some specific guidelines for the kernel documentation:
|
|||
the order as encountered."), having the higher levels the same overall makes
|
||||
it easier to follow the documents.
|
||||
|
||||
* For inserting fixed width text blocks (for code examples, use case
|
||||
examples, etc.), use ``::`` for anything that doesn't really benefit
|
||||
from syntax highlighting, especially short snippets. Use
|
||||
``.. code-block:: <language>`` for longer code blocks that benefit
|
||||
from highlighting.
|
||||
|
||||
|
||||
the C domain
|
||||
------------
|
||||
|
@ -217,3 +233,96 @@ Rendered as:
|
|||
* .. _`last row`:
|
||||
|
||||
- column 3
|
||||
|
||||
|
||||
Figures & Images
|
||||
================
|
||||
|
||||
If you want to add an image, you should use the ``kernel-figure`` and
|
||||
``kernel-image`` directives. E.g. to insert a figure with a scalable
|
||||
image format use SVG (:ref:`svg_image_example`)::
|
||||
|
||||
.. kernel-figure:: svg_image.svg
|
||||
:alt: simple SVG image
|
||||
|
||||
SVG image example
|
||||
|
||||
.. _svg_image_example:
|
||||
|
||||
.. kernel-figure:: svg_image.svg
|
||||
:alt: simple SVG image
|
||||
|
||||
SVG image example
|
||||
|
||||
The kernel figure (and image) directive support **DOT** formated files, see
|
||||
|
||||
* DOT: http://graphviz.org/pdf/dotguide.pdf
|
||||
* Graphviz: http://www.graphviz.org/content/dot-language
|
||||
|
||||
A simple example (:ref:`hello_dot_file`)::
|
||||
|
||||
.. kernel-figure:: hello.dot
|
||||
:alt: hello world
|
||||
|
||||
DOT's hello world example
|
||||
|
||||
.. _hello_dot_file:
|
||||
|
||||
.. kernel-figure:: hello.dot
|
||||
:alt: hello world
|
||||
|
||||
DOT's hello world example
|
||||
|
||||
Embed *render* markups (or languages) like Graphviz's **DOT** is provided by the
|
||||
``kernel-render`` directives.::
|
||||
|
||||
.. kernel-render:: DOT
|
||||
:alt: foobar digraph
|
||||
:caption: Embedded **DOT** (Graphviz) code
|
||||
|
||||
digraph foo {
|
||||
"bar" -> "baz";
|
||||
}
|
||||
|
||||
How this will be rendered depends on the installed tools. If Graphviz is
|
||||
installed, you will see an vector image. If not the raw markup is inserted as
|
||||
*literal-block* (:ref:`hello_dot_render`).
|
||||
|
||||
.. _hello_dot_render:
|
||||
|
||||
.. kernel-render:: DOT
|
||||
:alt: foobar digraph
|
||||
:caption: Embedded **DOT** (Graphviz) code
|
||||
|
||||
digraph foo {
|
||||
"bar" -> "baz";
|
||||
}
|
||||
|
||||
The *render* directive has all the options known from the *figure* directive,
|
||||
plus option ``caption``. If ``caption`` has a value, a *figure* node is
|
||||
inserted. If not, a *image* node is inserted. A ``caption`` is also needed, if
|
||||
you want to refer it (:ref:`hello_svg_render`).
|
||||
|
||||
Embedded **SVG**::
|
||||
|
||||
.. kernel-render:: SVG
|
||||
:caption: Embedded **SVG** markup
|
||||
:alt: so-nw-arrow
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" ...>
|
||||
...
|
||||
</svg>
|
||||
|
||||
.. _hello_svg_render:
|
||||
|
||||
.. kernel-render:: SVG
|
||||
:caption: Embedded **SVG** markup
|
||||
:alt: so-nw-arrow
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1" baseProfile="full" width="70px" height="40px" viewBox="0 0 700 400">
|
||||
<line x1="180" y1="370" x2="500" y2="50" stroke="black" stroke-width="15px"/>
|
||||
<polygon points="585 0 525 25 585 50" transform="rotate(135 525 25)"/>
|
||||
</svg>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- originate: https://commons.wikimedia.org/wiki/File:Variable_Resistor.svg -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1" baseProfile="full"
|
||||
width="70px" height="40px" viewBox="0 0 700 400">
|
||||
<line x1="0" y1="200" x2="700" y2="200" stroke="black" stroke-width="20px"/>
|
||||
<rect x="100" y="100" width="500" height="200" fill="white" stroke="black" stroke-width="20px"/>
|
||||
<line x1="180" y1="370" x2="500" y2="50" stroke="black" stroke-width="15px"/>
|
||||
<polygon points="585 0 525 25 585 50" transform="rotate(135 525 25)"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 580 B |
|
@ -140,12 +140,12 @@ Device Instance and Driver Handling
|
|||
.. kernel-doc:: drivers/gpu/drm/drm_drv.c
|
||||
:doc: driver instance overview
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_drv.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: include/drm/drm_drv.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_drv.c
|
||||
:export:
|
||||
|
||||
Driver Load
|
||||
-----------
|
||||
|
||||
|
@ -243,61 +243,15 @@ drivers.
|
|||
Open/Close, File Operations and IOCTLs
|
||||
======================================
|
||||
|
||||
Open and Close
|
||||
--------------
|
||||
|
||||
Open and close handlers. None of those methods are mandatory::
|
||||
|
||||
int (*firstopen) (struct drm_device *);
|
||||
void (*lastclose) (struct drm_device *);
|
||||
int (*open) (struct drm_device *, struct drm_file *);
|
||||
void (*preclose) (struct drm_device *, struct drm_file *);
|
||||
void (*postclose) (struct drm_device *, struct drm_file *);
|
||||
|
||||
The firstopen method is called by the DRM core for legacy UMS (User Mode
|
||||
Setting) drivers only when an application opens a device that has no
|
||||
other opened file handle. UMS drivers can implement it to acquire device
|
||||
resources. KMS drivers can't use the method and must acquire resources
|
||||
in the load method instead.
|
||||
|
||||
Similarly the lastclose method is called when the last application
|
||||
holding a file handle opened on the device closes it, for both UMS and
|
||||
KMS drivers. Additionally, the method is also called at module unload
|
||||
time or, for hot-pluggable devices, when the device is unplugged. The
|
||||
firstopen and lastclose calls can thus be unbalanced.
|
||||
|
||||
The open method is called every time the device is opened by an
|
||||
application. Drivers can allocate per-file private data in this method
|
||||
and store them in the struct :c:type:`struct drm_file
|
||||
<drm_file>` driver_priv field. Note that the open method is
|
||||
called before firstopen.
|
||||
|
||||
The close operation is split into preclose and postclose methods.
|
||||
Drivers must stop and cleanup all per-file operations in the preclose
|
||||
method. For instance pending vertical blanking and page flip events must
|
||||
be cancelled. No per-file operation is allowed on the file handle after
|
||||
returning from the preclose method.
|
||||
|
||||
Finally the postclose method is called as the last step of the close
|
||||
operation, right before calling the lastclose method if no other open
|
||||
file handle exists for the device. Drivers that have allocated per-file
|
||||
private data in the open method should free it here.
|
||||
|
||||
The lastclose method should restore CRTC and plane properties to default
|
||||
value, so that a subsequent open of the device will not inherit state
|
||||
from the previous user. It can also be used to execute delayed power
|
||||
switching state changes, e.g. in conjunction with the :ref:`vga_switcheroo`
|
||||
infrastructure. Beyond that KMS drivers should not do any
|
||||
further cleanup. Only legacy UMS drivers might need to clean up device
|
||||
state so that the vga console or an independent fbdev driver could take
|
||||
over.
|
||||
|
||||
File Operations
|
||||
---------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_file.c
|
||||
:doc: file operations
|
||||
|
||||
.. kernel-doc:: include/drm/drm_file.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_file.c
|
||||
:export:
|
||||
|
||||
|
|
|
@ -37,10 +37,12 @@ Modeset Helper Reference for Common Vtables
|
|||
===========================================
|
||||
|
||||
.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
|
||||
:internal:
|
||||
:doc: overview
|
||||
|
||||
.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
|
||||
:doc: overview
|
||||
:internal:
|
||||
|
||||
.. _drm_atomic_helper:
|
||||
|
||||
Atomic Modeset Helper Functions Reference
|
||||
=========================================
|
||||
|
@ -84,27 +86,27 @@ Legacy CRTC/Modeset Helper Functions Reference
|
|||
Simple KMS Helper Reference
|
||||
===========================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
|
||||
:doc: overview
|
||||
|
||||
.. kernel-doc:: include/drm/drm_simple_kms_helper.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
|
||||
:doc: overview
|
||||
|
||||
fbdev Helper Functions Reference
|
||||
================================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
|
||||
:doc: fbdev helpers
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: include/drm/drm_fb_helper.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
|
||||
:export:
|
||||
|
||||
Framebuffer CMA Helper Functions Reference
|
||||
==========================================
|
||||
|
||||
|
@ -114,6 +116,8 @@ Framebuffer CMA Helper Functions Reference
|
|||
.. kernel-doc:: drivers/gpu/drm/drm_fb_cma_helper.c
|
||||
:export:
|
||||
|
||||
.. _drm_bridges:
|
||||
|
||||
Bridges
|
||||
=======
|
||||
|
||||
|
@ -139,18 +143,20 @@ Bridge Helper Reference
|
|||
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
|
||||
:export:
|
||||
|
||||
.. _drm_panel_helper:
|
||||
|
||||
Panel Helper Reference
|
||||
======================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
|
||||
:doc: drm panel
|
||||
|
||||
.. kernel-doc:: include/drm/drm_panel.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
|
||||
:doc: drm panel
|
||||
|
||||
Display Port Helper Functions Reference
|
||||
=======================================
|
||||
|
||||
|
@ -217,6 +223,18 @@ EDID Helper Functions Reference
|
|||
.. kernel-doc:: drivers/gpu/drm/drm_edid.c
|
||||
:export:
|
||||
|
||||
SCDC Helper Functions Reference
|
||||
===============================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_scdc_helper.c
|
||||
:doc: scdc helpers
|
||||
|
||||
.. kernel-doc:: include/drm/drm_scdc_helper.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_scdc_helper.c
|
||||
:export:
|
||||
|
||||
Rectangle Utilities Reference
|
||||
=============================
|
||||
|
||||
|
|
|
@ -15,35 +15,271 @@ be setup by initializing the following fields.
|
|||
- struct drm_mode_config_funcs \*funcs;
|
||||
Mode setting functions.
|
||||
|
||||
Mode Configuration
|
||||
Overview
|
||||
========
|
||||
|
||||
.. kernel-render:: DOT
|
||||
:alt: KMS Display Pipeline
|
||||
:caption: KMS Display Pipeline Overview
|
||||
|
||||
digraph "KMS" {
|
||||
node [shape=box]
|
||||
|
||||
subgraph cluster_static {
|
||||
style=dashed
|
||||
label="Static Objects"
|
||||
|
||||
node [bgcolor=grey style=filled]
|
||||
"drm_plane A" -> "drm_crtc"
|
||||
"drm_plane B" -> "drm_crtc"
|
||||
"drm_crtc" -> "drm_encoder A"
|
||||
"drm_crtc" -> "drm_encoder B"
|
||||
}
|
||||
|
||||
subgraph cluster_user_created {
|
||||
style=dashed
|
||||
label="Userspace-Created"
|
||||
|
||||
node [shape=oval]
|
||||
"drm_framebuffer 1" -> "drm_plane A"
|
||||
"drm_framebuffer 2" -> "drm_plane B"
|
||||
}
|
||||
|
||||
subgraph cluster_connector {
|
||||
style=dashed
|
||||
label="Hotpluggable"
|
||||
|
||||
"drm_encoder A" -> "drm_connector A"
|
||||
"drm_encoder B" -> "drm_connector B"
|
||||
}
|
||||
}
|
||||
|
||||
The basic object structure KMS presents to userspace is fairly simple.
|
||||
Framebuffers (represented by :c:type:`struct drm_framebuffer <drm_framebuffer>`,
|
||||
see `Frame Buffer Abstraction`_) feed into planes. One or more (or even no)
|
||||
planes feed their pixel data into a CRTC (represented by :c:type:`struct
|
||||
drm_crtc <drm_crtc>`, see `CRTC Abstraction`_) for blending. The precise
|
||||
blending step is explained in more detail in `Plane Composition Properties`_ and
|
||||
related chapters.
|
||||
|
||||
For the output routing the first step is encoders (represented by
|
||||
:c:type:`struct drm_encoder <drm_encoder>`, see `Encoder Abstraction`_). Those
|
||||
are really just internal artifacts of the helper libraries used to implement KMS
|
||||
drivers. Besides that they make it unecessarily more complicated for userspace
|
||||
to figure out which connections between a CRTC and a connector are possible, and
|
||||
what kind of cloning is supported, they serve no purpose in the userspace API.
|
||||
Unfortunately encoders have been exposed to userspace, hence can't remove them
|
||||
at this point. Futhermore the exposed restrictions are often wrongly set by
|
||||
drivers, and in many cases not powerful enough to express the real restrictions.
|
||||
A CRTC can be connected to multiple encoders, and for an active CRTC there must
|
||||
be at least one encoder.
|
||||
|
||||
The final, and real, endpoint in the display chain is the connector (represented
|
||||
by :c:type:`struct drm_connector <drm_connector>`, see `Connector
|
||||
Abstraction`_). Connectors can have different possible encoders, but the kernel
|
||||
driver selects which encoder to use for each connector. The use case is DVI,
|
||||
which could switch between an analog and a digital encoder. Encoders can also
|
||||
drive multiple different connectors. There is exactly one active connector for
|
||||
every active encoder.
|
||||
|
||||
Internally the output pipeline is a bit more complex and matches today's
|
||||
hardware more closely:
|
||||
|
||||
.. kernel-render:: DOT
|
||||
:alt: KMS Output Pipeline
|
||||
:caption: KMS Output Pipeline
|
||||
|
||||
digraph "Output Pipeline" {
|
||||
node [shape=box]
|
||||
|
||||
subgraph {
|
||||
"drm_crtc" [bgcolor=grey style=filled]
|
||||
}
|
||||
|
||||
subgraph cluster_internal {
|
||||
style=dashed
|
||||
label="Internal Pipeline"
|
||||
{
|
||||
node [bgcolor=grey style=filled]
|
||||
"drm_encoder A";
|
||||
"drm_encoder B";
|
||||
"drm_encoder C";
|
||||
}
|
||||
|
||||
{
|
||||
node [bgcolor=grey style=filled]
|
||||
"drm_encoder B" -> "drm_bridge B"
|
||||
"drm_encoder C" -> "drm_bridge C1"
|
||||
"drm_bridge C1" -> "drm_bridge C2";
|
||||
}
|
||||
}
|
||||
|
||||
"drm_crtc" -> "drm_encoder A"
|
||||
"drm_crtc" -> "drm_encoder B"
|
||||
"drm_crtc" -> "drm_encoder C"
|
||||
|
||||
|
||||
subgraph cluster_output {
|
||||
style=dashed
|
||||
label="Outputs"
|
||||
|
||||
"drm_encoder A" -> "drm_connector A";
|
||||
"drm_bridge B" -> "drm_connector B";
|
||||
"drm_bridge C2" -> "drm_connector C";
|
||||
|
||||
"drm_panel"
|
||||
}
|
||||
}
|
||||
|
||||
Internally two additional helper objects come into play. First, to be able to
|
||||
share code for encoders (sometimes on the same SoC, sometimes off-chip) one or
|
||||
more :ref:`drm_bridges` (represented by :c:type:`struct drm_bridge
|
||||
<drm_bridge>`) can be linked to an encoder. This link is static and cannot be
|
||||
changed, which means the cross-bar (if there is any) needs to be mapped between
|
||||
the CRTC and any encoders. Often for drivers with bridges there's no code left
|
||||
at the encoder level. Atomic drivers can leave out all the encoder callbacks to
|
||||
essentially only leave a dummy routing object behind, which is needed for
|
||||
backwards compatibility since encoders are exposed to userspace.
|
||||
|
||||
The second object is for panels, represented by :c:type:`struct drm_panel
|
||||
<drm_panel>`, see :ref:`drm_panel_helper`. Panels do not have a fixed binding
|
||||
point, but are generally linked to the driver private structure that embeds
|
||||
:c:type:`struct drm_connector <drm_connector>`.
|
||||
|
||||
Note that currently the bridge chaining and interactions with connectors and
|
||||
panels are still in-flux and not really fully sorted out yet.
|
||||
|
||||
KMS Core Structures and Functions
|
||||
=================================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_mode_config.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: include/drm/drm_mode_config.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_mode_config.c
|
||||
:export:
|
||||
|
||||
Modeset Base Object Abstraction
|
||||
===============================
|
||||
|
||||
.. kernel-render:: DOT
|
||||
:alt: Mode Objects and Properties
|
||||
:caption: Mode Objects and Properties
|
||||
|
||||
digraph {
|
||||
node [shape=box]
|
||||
|
||||
"drm_property A" -> "drm_mode_object A"
|
||||
"drm_property A" -> "drm_mode_object B"
|
||||
"drm_property B" -> "drm_mode_object A"
|
||||
}
|
||||
|
||||
The base structure for all KMS objects is :c:type:`struct drm_mode_object
|
||||
<drm_mode_object>`. One of the base services it provides is tracking properties,
|
||||
which are especially important for the atomic IOCTL (see `Atomic Mode
|
||||
Setting`_). The somewhat surprising part here is that properties are not
|
||||
directly instantiated on each object, but free-standing mode objects themselves,
|
||||
represented by :c:type:`struct drm_property <drm_property>`, which only specify
|
||||
the type and value range of a property. Any given property can be attached
|
||||
multiple times to different objects using :c:func:`drm_object_attach_property()
|
||||
<drm_object_attach_property>`.
|
||||
|
||||
.. kernel-doc:: include/drm/drm_mode_object.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_mode_object.c
|
||||
:export:
|
||||
|
||||
Atomic Mode Setting Function Reference
|
||||
======================================
|
||||
Atomic Mode Setting
|
||||
===================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_atomic.c
|
||||
:export:
|
||||
|
||||
.. kernel-render:: DOT
|
||||
:alt: Mode Objects and Properties
|
||||
:caption: Mode Objects and Properties
|
||||
|
||||
digraph {
|
||||
node [shape=box]
|
||||
|
||||
subgraph cluster_state {
|
||||
style=dashed
|
||||
label="Free-standing state"
|
||||
|
||||
"drm_atomic_state" -> "duplicated drm_plane_state A"
|
||||
"drm_atomic_state" -> "duplicated drm_plane_state B"
|
||||
"drm_atomic_state" -> "duplicated drm_crtc_state"
|
||||
"drm_atomic_state" -> "duplicated drm_connector_state"
|
||||
"drm_atomic_state" -> "duplicated driver private state"
|
||||
}
|
||||
|
||||
subgraph cluster_current {
|
||||
style=dashed
|
||||
label="Current state"
|
||||
|
||||
"drm_device" -> "drm_plane A"
|
||||
"drm_device" -> "drm_plane B"
|
||||
"drm_device" -> "drm_crtc"
|
||||
"drm_device" -> "drm_connector"
|
||||
"drm_device" -> "driver private object"
|
||||
|
||||
"drm_plane A" -> "drm_plane_state A"
|
||||
"drm_plane B" -> "drm_plane_state B"
|
||||
"drm_crtc" -> "drm_crtc_state"
|
||||
"drm_connector" -> "drm_connector_state"
|
||||
"driver private object" -> "driver private state"
|
||||
}
|
||||
|
||||
"drm_atomic_state" -> "drm_device" [label="atomic_commit"]
|
||||
"duplicated drm_plane_state A" -> "drm_device"[style=invis]
|
||||
}
|
||||
|
||||
Atomic provides transactional modeset (including planes) updates, but a
|
||||
bit differently from the usual transactional approach of try-commit and
|
||||
rollback:
|
||||
|
||||
- Firstly, no hardware changes are allowed when the commit would fail. This
|
||||
allows us to implement the DRM_MODE_ATOMIC_TEST_ONLY mode, which allows
|
||||
userspace to explore whether certain configurations would work or not.
|
||||
|
||||
- This would still allow setting and rollback of just the software state,
|
||||
simplifying conversion of existing drivers. But auditing drivers for
|
||||
correctness of the atomic_check code becomes really hard with that: Rolling
|
||||
back changes in data structures all over the place is hard to get right.
|
||||
|
||||
- Lastly, for backwards compatibility and to support all use-cases, atomic
|
||||
updates need to be incremental and be able to execute in parallel. Hardware
|
||||
doesn't always allow it, but where possible plane updates on different CRTCs
|
||||
should not interfere, and not get stalled due to output routing changing on
|
||||
different CRTCs.
|
||||
|
||||
Taken all together there's two consequences for the atomic design:
|
||||
|
||||
- The overall state is split up into per-object state structures:
|
||||
:c:type:`struct drm_plane_state <drm_plane_state>` for planes, :c:type:`struct
|
||||
drm_crtc_state <drm_crtc_state>` for CRTCs and :c:type:`struct
|
||||
drm_connector_state <drm_connector_state>` for connectors. These are the only
|
||||
objects with userspace-visible and settable state. For internal state drivers
|
||||
can subclass these structures through embeddeding, or add entirely new state
|
||||
structures for their globally shared hardware functions.
|
||||
|
||||
- An atomic update is assembled and validated as an entirely free-standing pile
|
||||
of structures within the :c:type:`drm_atomic_state <drm_atomic_state>`
|
||||
container. Again drivers can subclass that container for their own state
|
||||
structure tracking needs. Only when a state is committed is it applied to the
|
||||
driver and modeset objects. This way rolling back an update boils down to
|
||||
releasing memory and unreferencing objects like framebuffers.
|
||||
|
||||
Read on in this chapter, and also in :ref:`drm_atomic_helper` for more detailed
|
||||
coverage of specific topics.
|
||||
|
||||
Atomic Mode Setting Function Reference
|
||||
--------------------------------------
|
||||
|
||||
.. kernel-doc:: include/drm/drm_atomic.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_atomic.c
|
||||
:export:
|
||||
|
||||
CRTC Abstraction
|
||||
================
|
||||
|
||||
|
@ -68,12 +304,12 @@ Frame Buffer Abstraction
|
|||
Frame Buffer Functions Reference
|
||||
--------------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_framebuffer.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: include/drm/drm_framebuffer.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_framebuffer.c
|
||||
:export:
|
||||
|
||||
DRM Format Handling
|
||||
===================
|
||||
|
||||
|
@ -376,8 +612,8 @@ operation handler.
|
|||
Vertical Blanking and Interrupt Handling Functions Reference
|
||||
------------------------------------------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_irq.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: include/drm/drm_irq.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_irq.c
|
||||
:export:
|
||||
|
|
|
@ -365,36 +365,36 @@ from the client in libdrm.
|
|||
GEM Function Reference
|
||||
----------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gem.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: include/drm/drm_gem.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gem.c
|
||||
:export:
|
||||
|
||||
GEM CMA Helper Functions Reference
|
||||
----------------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
|
||||
:doc: cma helpers
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: include/drm/drm_gem_cma_helper.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
|
||||
:export:
|
||||
|
||||
VMA Offset Manager
|
||||
==================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c
|
||||
:doc: vma offset manager
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: include/drm/drm_vma_manager.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c
|
||||
:export:
|
||||
|
||||
PRIME Buffer Sharing
|
||||
====================
|
||||
|
||||
|
@ -473,12 +473,12 @@ LRU Scan/Eviction Support
|
|||
DRM MM Range Allocator Function References
|
||||
------------------------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_mm.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: include/drm/drm_mm.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_mm.c
|
||||
:export:
|
||||
|
||||
DRM Cache Handling
|
||||
==================
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ libdrm Device Lookup
|
|||
:doc: getunique and setversion story
|
||||
|
||||
|
||||
.. _drm_primary_node:
|
||||
|
||||
Primary Nodes, DRM Master and Authentication
|
||||
============================================
|
||||
|
||||
|
@ -103,6 +105,8 @@ is already rather painful for the DRM subsystem, with multiple different uAPIs
|
|||
for the same thing co-existing. If we add a few more complete mistakes into the
|
||||
mix every year it would be entirely unmanageable.
|
||||
|
||||
.. _drm_render_node:
|
||||
|
||||
Render nodes
|
||||
============
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ should contain the phy name as given in the dt data and in the case of
|
|||
non-dt boot, it should contain the label of the PHY. The two
|
||||
devm_phy_get associates the device with the PHY using devres on
|
||||
successful PHY get. On driver detach, release function is invoked on
|
||||
the the devres data and devres data is freed. phy_optional_get and
|
||||
the devres data and devres data is freed. phy_optional_get and
|
||||
devm_phy_optional_get should be used when the phy is optional. These
|
||||
two functions will never return -ENODEV, but instead returns NULL when
|
||||
the phy cannot be found.Some generic drivers, such as ehci, may use multiple
|
||||
|
|
|
@ -318,9 +318,10 @@ PDF outputs, it is recommended to use version 1.4.6.
|
|||
.. note::
|
||||
|
||||
Please notice that, for PDF and LaTeX output, you'll also need ``XeLaTeX``
|
||||
version 3.14159265. Depending on the distribution, you may also need
|
||||
to install a series of ``texlive`` packages that provide the minimal
|
||||
set of functionalities required for ``XeLaTex`` to work.
|
||||
version 3.14159265. Depending on the distribution, you may also need to
|
||||
install a series of ``texlive`` packages that provide the minimal set of
|
||||
functionalities required for ``XeLaTex`` to work. For PDF output you'll also
|
||||
need ``convert(1)`` from ImageMagick (https://www.imagemagick.org).
|
||||
|
||||
Other tools
|
||||
-----------
|
||||
|
|
|
@ -0,0 +1,551 @@
|
|||
# -*- coding: utf-8; mode: python -*-
|
||||
# pylint: disable=C0103, R0903, R0912, R0915
|
||||
u"""
|
||||
scalable figure and image handling
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sphinx extension which implements scalable image handling.
|
||||
|
||||
:copyright: Copyright (C) 2016 Markus Heiser
|
||||
:license: GPL Version 2, June 1991 see Linux/COPYING for details.
|
||||
|
||||
The build for image formats depend on image's source format and output's
|
||||
destination format. This extension implement methods to simplify image
|
||||
handling from the author's POV. Directives like ``kernel-figure`` implement
|
||||
methods *to* always get the best output-format even if some tools are not
|
||||
installed. For more details take a look at ``convert_image(...)`` which is
|
||||
the core of all conversions.
|
||||
|
||||
* ``.. kernel-image``: for image handling / a ``.. image::`` replacement
|
||||
|
||||
* ``.. kernel-figure``: for figure handling / a ``.. figure::`` replacement
|
||||
|
||||
* ``.. kernel-render``: for render markup / a concept to embed *render*
|
||||
markups (or languages). Supported markups (see ``RENDER_MARKUP_EXT``)
|
||||
|
||||
- ``DOT``: render embedded Graphviz's **DOC**
|
||||
- ``SVG``: render embedded Scalable Vector Graphics (**SVG**)
|
||||
- ... *developable*
|
||||
|
||||
Used tools:
|
||||
|
||||
* ``dot(1)``: Graphviz (http://www.graphviz.org). If Graphviz is not
|
||||
available, the DOT language is inserted as literal-block.
|
||||
|
||||
* SVG to PDF: To generate PDF, you need at least one of this tools:
|
||||
|
||||
- ``convert(1)``: ImageMagick (https://www.imagemagick.org)
|
||||
|
||||
List of customizations:
|
||||
|
||||
* generate PDF from SVG / used by PDF (LaTeX) builder
|
||||
|
||||
* generate SVG (html-builder) and PDF (latex-builder) from DOT files.
|
||||
DOT: see http://www.graphviz.org/content/dot-language
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
from os import path
|
||||
import subprocess
|
||||
from hashlib import sha1
|
||||
import sys
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.statemachine import ViewList
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst.directives import images
|
||||
import sphinx
|
||||
|
||||
from sphinx.util.nodes import clean_astext
|
||||
from six import iteritems
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
_unicode = str
|
||||
else:
|
||||
_unicode = unicode
|
||||
|
||||
# Get Sphinx version
|
||||
major, minor, patch = sphinx.version_info[:3]
|
||||
if major == 1 and minor > 3:
|
||||
# patches.Figure only landed in Sphinx 1.4
|
||||
from sphinx.directives.patches import Figure # pylint: disable=C0413
|
||||
else:
|
||||
Figure = images.Figure
|
||||
|
||||
__version__ = '1.0.0'
|
||||
|
||||
# simple helper
|
||||
# -------------
|
||||
|
||||
def which(cmd):
|
||||
"""Searches the ``cmd`` in the ``PATH`` enviroment.
|
||||
|
||||
This *which* searches the PATH for executable ``cmd`` . First match is
|
||||
returned, if nothing is found, ``None` is returned.
|
||||
"""
|
||||
envpath = os.environ.get('PATH', None) or os.defpath
|
||||
for folder in envpath.split(os.pathsep):
|
||||
fname = folder + os.sep + cmd
|
||||
if path.isfile(fname):
|
||||
return fname
|
||||
|
||||
def mkdir(folder, mode=0o775):
|
||||
if not path.isdir(folder):
|
||||
os.makedirs(folder, mode)
|
||||
|
||||
def file2literal(fname):
|
||||
with open(fname, "r") as src:
|
||||
data = src.read()
|
||||
node = nodes.literal_block(data, data)
|
||||
return node
|
||||
|
||||
def isNewer(path1, path2):
|
||||
"""Returns True if ``path1`` is newer than ``path2``
|
||||
|
||||
If ``path1`` exists and is newer than ``path2`` the function returns
|
||||
``True`` is returned otherwise ``False``
|
||||
"""
|
||||
return (path.exists(path1)
|
||||
and os.stat(path1).st_ctime > os.stat(path2).st_ctime)
|
||||
|
||||
def pass_handle(self, node): # pylint: disable=W0613
|
||||
pass
|
||||
|
||||
# setup conversion tools and sphinx extension
|
||||
# -------------------------------------------
|
||||
|
||||
# Graphviz's dot(1) support
|
||||
dot_cmd = None
|
||||
|
||||
# ImageMagick' convert(1) support
|
||||
convert_cmd = None
|
||||
|
||||
|
||||
def setup(app):
|
||||
# check toolchain first
|
||||
app.connect('builder-inited', setupTools)
|
||||
|
||||
# image handling
|
||||
app.add_directive("kernel-image", KernelImage)
|
||||
app.add_node(kernel_image,
|
||||
html = (visit_kernel_image, pass_handle),
|
||||
latex = (visit_kernel_image, pass_handle),
|
||||
texinfo = (visit_kernel_image, pass_handle),
|
||||
text = (visit_kernel_image, pass_handle),
|
||||
man = (visit_kernel_image, pass_handle), )
|
||||
|
||||
# figure handling
|
||||
app.add_directive("kernel-figure", KernelFigure)
|
||||
app.add_node(kernel_figure,
|
||||
html = (visit_kernel_figure, pass_handle),
|
||||
latex = (visit_kernel_figure, pass_handle),
|
||||
texinfo = (visit_kernel_figure, pass_handle),
|
||||
text = (visit_kernel_figure, pass_handle),
|
||||
man = (visit_kernel_figure, pass_handle), )
|
||||
|
||||
# render handling
|
||||
app.add_directive('kernel-render', KernelRender)
|
||||
app.add_node(kernel_render,
|
||||
html = (visit_kernel_render, pass_handle),
|
||||
latex = (visit_kernel_render, pass_handle),
|
||||
texinfo = (visit_kernel_render, pass_handle),
|
||||
text = (visit_kernel_render, pass_handle),
|
||||
man = (visit_kernel_render, pass_handle), )
|
||||
|
||||
app.connect('doctree-read', add_kernel_figure_to_std_domain)
|
||||
|
||||
return dict(
|
||||
version = __version__,
|
||||
parallel_read_safe = True,
|
||||
parallel_write_safe = True
|
||||
)
|
||||
|
||||
|
||||
def setupTools(app):
|
||||
u"""
|
||||
Check available build tools and log some *verbose* messages.
|
||||
|
||||
This function is called once, when the builder is initiated.
|
||||
"""
|
||||
global dot_cmd, convert_cmd # pylint: disable=W0603
|
||||
app.verbose("kfigure: check installed tools ...")
|
||||
|
||||
dot_cmd = which('dot')
|
||||
convert_cmd = which('convert')
|
||||
|
||||
if dot_cmd:
|
||||
app.verbose("use dot(1) from: " + dot_cmd)
|
||||
else:
|
||||
app.warn("dot(1) not found, for better output quality install "
|
||||
"graphviz from http://www.graphviz.org")
|
||||
if convert_cmd:
|
||||
app.verbose("use convert(1) from: " + convert_cmd)
|
||||
else:
|
||||
app.warn(
|
||||
"convert(1) not found, for SVG to PDF conversion install "
|
||||
"ImageMagick (https://www.imagemagick.org)")
|
||||
|
||||
|
||||
# integrate conversion tools
|
||||
# --------------------------
|
||||
|
||||
RENDER_MARKUP_EXT = {
|
||||
# The '.ext' must be handled by convert_image(..) function's *in_ext* input.
|
||||
# <name> : <.ext>
|
||||
'DOT' : '.dot',
|
||||
'SVG' : '.svg'
|
||||
}
|
||||
|
||||
def convert_image(img_node, translator, src_fname=None):
|
||||
"""Convert a image node for the builder.
|
||||
|
||||
Different builder prefer different image formats, e.g. *latex* builder
|
||||
prefer PDF while *html* builder prefer SVG format for images.
|
||||
|
||||
This function handles output image formats in dependence of source the
|
||||
format (of the image) and the translator's output format.
|
||||
"""
|
||||
app = translator.builder.app
|
||||
|
||||
fname, in_ext = path.splitext(path.basename(img_node['uri']))
|
||||
if src_fname is None:
|
||||
src_fname = path.join(translator.builder.srcdir, img_node['uri'])
|
||||
if not path.exists(src_fname):
|
||||
src_fname = path.join(translator.builder.outdir, img_node['uri'])
|
||||
|
||||
dst_fname = None
|
||||
|
||||
# in kernel builds, use 'make SPHINXOPTS=-v' to see verbose messages
|
||||
|
||||
app.verbose('assert best format for: ' + img_node['uri'])
|
||||
|
||||
if in_ext == '.dot':
|
||||
|
||||
if not dot_cmd:
|
||||
app.verbose("dot from graphviz not available / include DOT raw.")
|
||||
img_node.replace_self(file2literal(src_fname))
|
||||
|
||||
elif translator.builder.format == 'latex':
|
||||
dst_fname = path.join(translator.builder.outdir, fname + '.pdf')
|
||||
img_node['uri'] = fname + '.pdf'
|
||||
img_node['candidates'] = {'*': fname + '.pdf'}
|
||||
|
||||
|
||||
elif translator.builder.format == 'html':
|
||||
dst_fname = path.join(
|
||||
translator.builder.outdir,
|
||||
translator.builder.imagedir,
|
||||
fname + '.svg')
|
||||
img_node['uri'] = path.join(
|
||||
translator.builder.imgpath, fname + '.svg')
|
||||
img_node['candidates'] = {
|
||||
'*': path.join(translator.builder.imgpath, fname + '.svg')}
|
||||
|
||||
else:
|
||||
# all other builder formats will include DOT as raw
|
||||
img_node.replace_self(file2literal(src_fname))
|
||||
|
||||
elif in_ext == '.svg':
|
||||
|
||||
if translator.builder.format == 'latex':
|
||||
if convert_cmd is None:
|
||||
app.verbose("no SVG to PDF conversion available / include SVG raw.")
|
||||
img_node.replace_self(file2literal(src_fname))
|
||||
else:
|
||||
dst_fname = path.join(translator.builder.outdir, fname + '.pdf')
|
||||
img_node['uri'] = fname + '.pdf'
|
||||
img_node['candidates'] = {'*': fname + '.pdf'}
|
||||
|
||||
if dst_fname:
|
||||
# the builder needs not to copy one more time, so pop it if exists.
|
||||
translator.builder.images.pop(img_node['uri'], None)
|
||||
_name = dst_fname[len(translator.builder.outdir) + 1:]
|
||||
|
||||
if isNewer(dst_fname, src_fname):
|
||||
app.verbose("convert: {out}/%s already exists and is newer" % _name)
|
||||
|
||||
else:
|
||||
ok = False
|
||||
mkdir(path.dirname(dst_fname))
|
||||
|
||||
if in_ext == '.dot':
|
||||
app.verbose('convert DOT to: {out}/' + _name)
|
||||
ok = dot2format(app, src_fname, dst_fname)
|
||||
|
||||
elif in_ext == '.svg':
|
||||
app.verbose('convert SVG to: {out}/' + _name)
|
||||
ok = svg2pdf(app, src_fname, dst_fname)
|
||||
|
||||
if not ok:
|
||||
img_node.replace_self(file2literal(src_fname))
|
||||
|
||||
|
||||
def dot2format(app, dot_fname, out_fname):
|
||||
"""Converts DOT file to ``out_fname`` using ``dot(1)``.
|
||||
|
||||
* ``dot_fname`` pathname of the input DOT file, including extension ``.dot``
|
||||
* ``out_fname`` pathname of the output file, including format extension
|
||||
|
||||
The *format extension* depends on the ``dot`` command (see ``man dot``
|
||||
option ``-Txxx``). Normally you will use one of the following extensions:
|
||||
|
||||
- ``.ps`` for PostScript,
|
||||
- ``.svg`` or ``svgz`` for Structured Vector Graphics,
|
||||
- ``.fig`` for XFIG graphics and
|
||||
- ``.png`` or ``gif`` for common bitmap graphics.
|
||||
|
||||
"""
|
||||
out_format = path.splitext(out_fname)[1][1:]
|
||||
cmd = [dot_cmd, '-T%s' % out_format, dot_fname]
|
||||
exit_code = 42
|
||||
|
||||
with open(out_fname, "w") as out:
|
||||
exit_code = subprocess.call(cmd, stdout = out)
|
||||
if exit_code != 0:
|
||||
app.warn("Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
|
||||
return bool(exit_code == 0)
|
||||
|
||||
def svg2pdf(app, svg_fname, pdf_fname):
|
||||
"""Converts SVG to PDF with ``convert(1)`` command.
|
||||
|
||||
Uses ``convert(1)`` from ImageMagick (https://www.imagemagick.org) for
|
||||
conversion. Returns ``True`` on success and ``False`` if an error occurred.
|
||||
|
||||
* ``svg_fname`` pathname of the input SVG file with extension (``.svg``)
|
||||
* ``pdf_name`` pathname of the output PDF file with extension (``.pdf``)
|
||||
|
||||
"""
|
||||
cmd = [convert_cmd, svg_fname, pdf_fname]
|
||||
# use stdout and stderr from parent
|
||||
exit_code = subprocess.call(cmd)
|
||||
if exit_code != 0:
|
||||
app.warn("Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
|
||||
return bool(exit_code == 0)
|
||||
|
||||
|
||||
# image handling
|
||||
# ---------------------
|
||||
|
||||
def visit_kernel_image(self, node): # pylint: disable=W0613
|
||||
"""Visitor of the ``kernel_image`` Node.
|
||||
|
||||
Handles the ``image`` child-node with the ``convert_image(...)``.
|
||||
"""
|
||||
img_node = node[0]
|
||||
convert_image(img_node, self)
|
||||
|
||||
class kernel_image(nodes.image):
|
||||
"""Node for ``kernel-image`` directive."""
|
||||
pass
|
||||
|
||||
class KernelImage(images.Image):
|
||||
u"""KernelImage directive
|
||||
|
||||
Earns everything from ``.. image::`` directive, except *remote URI* and
|
||||
*glob* pattern. The KernelImage wraps a image node into a
|
||||
kernel_image node. See ``visit_kernel_image``.
|
||||
"""
|
||||
|
||||
def run(self):
|
||||
uri = self.arguments[0]
|
||||
if uri.endswith('.*') or uri.find('://') != -1:
|
||||
raise self.severe(
|
||||
'Error in "%s: %s": glob pattern and remote images are not allowed'
|
||||
% (self.name, uri))
|
||||
result = images.Image.run(self)
|
||||
if len(result) == 2 or isinstance(result[0], nodes.system_message):
|
||||
return result
|
||||
(image_node,) = result
|
||||
# wrap image node into a kernel_image node / see visitors
|
||||
node = kernel_image('', image_node)
|
||||
return [node]
|
||||
|
||||
# figure handling
|
||||
# ---------------------
|
||||
|
||||
def visit_kernel_figure(self, node): # pylint: disable=W0613
|
||||
"""Visitor of the ``kernel_figure`` Node.
|
||||
|
||||
Handles the ``image`` child-node with the ``convert_image(...)``.
|
||||
"""
|
||||
img_node = node[0][0]
|
||||
convert_image(img_node, self)
|
||||
|
||||
class kernel_figure(nodes.figure):
|
||||
"""Node for ``kernel-figure`` directive."""
|
||||
|
||||
class KernelFigure(Figure):
|
||||
u"""KernelImage directive
|
||||
|
||||
Earns everything from ``.. figure::`` directive, except *remote URI* and
|
||||
*glob* pattern. The KernelFigure wraps a figure node into a kernel_figure
|
||||
node. See ``visit_kernel_figure``.
|
||||
"""
|
||||
|
||||
def run(self):
|
||||
uri = self.arguments[0]
|
||||
if uri.endswith('.*') or uri.find('://') != -1:
|
||||
raise self.severe(
|
||||
'Error in "%s: %s":'
|
||||
' glob pattern and remote images are not allowed'
|
||||
% (self.name, uri))
|
||||
result = Figure.run(self)
|
||||
if len(result) == 2 or isinstance(result[0], nodes.system_message):
|
||||
return result
|
||||
(figure_node,) = result
|
||||
# wrap figure node into a kernel_figure node / see visitors
|
||||
node = kernel_figure('', figure_node)
|
||||
return [node]
|
||||
|
||||
|
||||
# render handling
|
||||
# ---------------------
|
||||
|
||||
def visit_kernel_render(self, node):
|
||||
"""Visitor of the ``kernel_render`` Node.
|
||||
|
||||
If rendering tools available, save the markup of the ``literal_block`` child
|
||||
node into a file and replace the ``literal_block`` node with a new created
|
||||
``image`` node, pointing to the saved markup file. Afterwards, handle the
|
||||
image child-node with the ``convert_image(...)``.
|
||||
"""
|
||||
app = self.builder.app
|
||||
srclang = node.get('srclang')
|
||||
|
||||
app.verbose('visit kernel-render node lang: "%s"' % (srclang))
|
||||
|
||||
tmp_ext = RENDER_MARKUP_EXT.get(srclang, None)
|
||||
if tmp_ext is None:
|
||||
app.warn('kernel-render: "%s" unknow / include raw.' % (srclang))
|
||||
return
|
||||
|
||||
if not dot_cmd and tmp_ext == '.dot':
|
||||
app.verbose("dot from graphviz not available / include raw.")
|
||||
return
|
||||
|
||||
literal_block = node[0]
|
||||
|
||||
code = literal_block.astext()
|
||||
hashobj = code.encode('utf-8') # str(node.attributes)
|
||||
fname = path.join('%s-%s' % (srclang, sha1(hashobj).hexdigest()))
|
||||
|
||||
tmp_fname = path.join(
|
||||
self.builder.outdir, self.builder.imagedir, fname + tmp_ext)
|
||||
|
||||
if not path.isfile(tmp_fname):
|
||||
mkdir(path.dirname(tmp_fname))
|
||||
with open(tmp_fname, "w") as out:
|
||||
out.write(code)
|
||||
|
||||
img_node = nodes.image(node.rawsource, **node.attributes)
|
||||
img_node['uri'] = path.join(self.builder.imgpath, fname + tmp_ext)
|
||||
img_node['candidates'] = {
|
||||
'*': path.join(self.builder.imgpath, fname + tmp_ext)}
|
||||
|
||||
literal_block.replace_self(img_node)
|
||||
convert_image(img_node, self, tmp_fname)
|
||||
|
||||
|
||||
class kernel_render(nodes.General, nodes.Inline, nodes.Element):
|
||||
"""Node for ``kernel-render`` directive."""
|
||||
pass
|
||||
|
||||
class KernelRender(Figure):
|
||||
u"""KernelRender directive
|
||||
|
||||
Render content by external tool. Has all the options known from the
|
||||
*figure* directive, plus option ``caption``. If ``caption`` has a
|
||||
value, a figure node with the *caption* is inserted. If not, a image node is
|
||||
inserted.
|
||||
|
||||
The KernelRender directive wraps the text of the directive into a
|
||||
literal_block node and wraps it into a kernel_render node. See
|
||||
``visit_kernel_render``.
|
||||
"""
|
||||
has_content = True
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = False
|
||||
|
||||
# earn options from 'figure'
|
||||
option_spec = Figure.option_spec.copy()
|
||||
option_spec['caption'] = directives.unchanged
|
||||
|
||||
def run(self):
|
||||
return [self.build_node()]
|
||||
|
||||
def build_node(self):
|
||||
|
||||
srclang = self.arguments[0].strip()
|
||||
if srclang not in RENDER_MARKUP_EXT.keys():
|
||||
return [self.state_machine.reporter.warning(
|
||||
'Unknow source language "%s", use one of: %s.' % (
|
||||
srclang, ",".join(RENDER_MARKUP_EXT.keys())),
|
||||
line=self.lineno)]
|
||||
|
||||
code = '\n'.join(self.content)
|
||||
if not code.strip():
|
||||
return [self.state_machine.reporter.warning(
|
||||
'Ignoring "%s" directive without content.' % (
|
||||
self.name),
|
||||
line=self.lineno)]
|
||||
|
||||
node = kernel_render()
|
||||
node['alt'] = self.options.get('alt','')
|
||||
node['srclang'] = srclang
|
||||
literal_node = nodes.literal_block(code, code)
|
||||
node += literal_node
|
||||
|
||||
caption = self.options.get('caption')
|
||||
if caption:
|
||||
# parse caption's content
|
||||
parsed = nodes.Element()
|
||||
self.state.nested_parse(
|
||||
ViewList([caption], source=''), self.content_offset, parsed)
|
||||
caption_node = nodes.caption(
|
||||
parsed[0].rawsource, '', *parsed[0].children)
|
||||
caption_node.source = parsed[0].source
|
||||
caption_node.line = parsed[0].line
|
||||
|
||||
figure_node = nodes.figure('', node)
|
||||
for k,v in self.options.items():
|
||||
figure_node[k] = v
|
||||
figure_node += caption_node
|
||||
|
||||
node = figure_node
|
||||
|
||||
return node
|
||||
|
||||
def add_kernel_figure_to_std_domain(app, doctree):
|
||||
"""Add kernel-figure anchors to 'std' domain.
|
||||
|
||||
The ``StandardDomain.process_doc(..)`` method does not know how to resolve
|
||||
the caption (label) of ``kernel-figure`` directive (it only knows about
|
||||
standard nodes, e.g. table, figure etc.). Without any additional handling
|
||||
this will result in a 'undefined label' for kernel-figures.
|
||||
|
||||
This handle adds labels of kernel-figure to the 'std' domain labels.
|
||||
"""
|
||||
|
||||
std = app.env.domains["std"]
|
||||
docname = app.env.docname
|
||||
labels = std.data["labels"]
|
||||
|
||||
for name, explicit in iteritems(doctree.nametypes):
|
||||
if not explicit:
|
||||
continue
|
||||
labelid = doctree.nameids[name]
|
||||
if labelid is None:
|
||||
continue
|
||||
node = doctree.ids[labelid]
|
||||
|
||||
if node.tagname == 'kernel_figure':
|
||||
for n in node.next_node():
|
||||
if n.tagname == 'caption':
|
||||
sectname = clean_astext(n)
|
||||
# add label to std domain
|
||||
labels[name] = docname, labelid, sectname
|
||||
break
|
|
@ -144,3 +144,29 @@ struct dma_fence_array *dma_fence_array_create(int num_fences,
|
|||
return array;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_fence_array_create);
|
||||
|
||||
/**
|
||||
* dma_fence_match_context - Check if all fences are from the given context
|
||||
* @fence: [in] fence or fence array
|
||||
* @context: [in] fence context to check all fences against
|
||||
*
|
||||
* Checks the provided fence or, for a fence array, all fences in the array
|
||||
* against the given context. Returns false if any fence is from a different
|
||||
* context.
|
||||
*/
|
||||
bool dma_fence_match_context(struct dma_fence *fence, u64 context)
|
||||
{
|
||||
struct dma_fence_array *array = to_dma_fence_array(fence);
|
||||
unsigned i;
|
||||
|
||||
if (!dma_fence_is_array(fence))
|
||||
return fence->context == context;
|
||||
|
||||
for (i = 0; i < array->num_fences; i++) {
|
||||
if (array->fences[i]->context != context)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_fence_match_context);
|
||||
|
|
|
@ -31,7 +31,8 @@ drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
|
|||
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
|
||||
drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
|
||||
drm_kms_helper_common.o drm_dp_dual_mode_helper.o \
|
||||
drm_simple_kms_helper.o drm_modeset_helper.o
|
||||
drm_simple_kms_helper.o drm_modeset_helper.o \
|
||||
drm_scdc_helper.o
|
||||
|
||||
drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
|
||||
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
|
||||
|
|
|
@ -239,17 +239,7 @@ static int hdlcd_debugfs_init(struct drm_minor *minor)
|
|||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = noop_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(fops);
|
||||
|
||||
static struct drm_driver hdlcd_driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
|
||||
|
|
|
@ -178,17 +178,7 @@ static void malidp_lastclose(struct drm_device *drm)
|
|||
drm_fbdev_cma_restore_mode(malidp->fbdev);
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = noop_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(fops);
|
||||
|
||||
static struct drm_driver malidp_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
|
||||
|
|
|
@ -60,16 +60,7 @@ static void armada_drm_lastclose(struct drm_device *dev)
|
|||
armada_fbdev_lastclose(dev);
|
||||
}
|
||||
|
||||
static const struct file_operations armada_drm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = drm_read,
|
||||
.poll = drm_poll,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.mmap = drm_gem_mmap,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
};
|
||||
DEFINE_DRM_GEM_FOPS(armada_drm_fops);
|
||||
|
||||
static struct drm_driver armada_drm_driver = {
|
||||
.lastclose = armada_drm_lastclose,
|
||||
|
|
|
@ -55,14 +55,12 @@ drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state)
|
|||
* @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
|
||||
* @event: pointer to the current page flip event
|
||||
* @id: CRTC id (returned by drm_crtc_index)
|
||||
* @enabled: CRTC state
|
||||
*/
|
||||
struct atmel_hlcdc_crtc {
|
||||
struct drm_crtc base;
|
||||
struct atmel_hlcdc_dc *dc;
|
||||
struct drm_pending_vblank_event *event;
|
||||
int id;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static inline struct atmel_hlcdc_crtc *
|
||||
|
@ -158,9 +156,6 @@ static void atmel_hlcdc_crtc_disable(struct drm_crtc *c)
|
|||
struct regmap *regmap = crtc->dc->hlcdc->regmap;
|
||||
unsigned int status;
|
||||
|
||||
if (!crtc->enabled)
|
||||
return;
|
||||
|
||||
drm_crtc_vblank_off(c);
|
||||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
@ -186,8 +181,6 @@ static void atmel_hlcdc_crtc_disable(struct drm_crtc *c)
|
|||
pm_runtime_allow(dev->dev);
|
||||
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
|
||||
crtc->enabled = false;
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_crtc_enable(struct drm_crtc *c)
|
||||
|
@ -197,9 +190,6 @@ static void atmel_hlcdc_crtc_enable(struct drm_crtc *c)
|
|||
struct regmap *regmap = crtc->dc->hlcdc->regmap;
|
||||
unsigned int status;
|
||||
|
||||
if (crtc->enabled)
|
||||
return;
|
||||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
pm_runtime_forbid(dev->dev);
|
||||
|
@ -226,29 +216,6 @@ static void atmel_hlcdc_crtc_enable(struct drm_crtc *c)
|
|||
pm_runtime_put_sync(dev->dev);
|
||||
|
||||
drm_crtc_vblank_on(c);
|
||||
|
||||
crtc->enabled = true;
|
||||
}
|
||||
|
||||
void atmel_hlcdc_crtc_suspend(struct drm_crtc *c)
|
||||
{
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
|
||||
if (crtc->enabled) {
|
||||
atmel_hlcdc_crtc_disable(c);
|
||||
/* save enable state for resume */
|
||||
crtc->enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void atmel_hlcdc_crtc_resume(struct drm_crtc *c)
|
||||
{
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
|
||||
if (crtc->enabled) {
|
||||
crtc->enabled = false;
|
||||
atmel_hlcdc_crtc_enable(c);
|
||||
}
|
||||
}
|
||||
|
||||
#define ATMEL_HLCDC_RGB444_OUTPUT BIT(0)
|
||||
|
|
|
@ -724,17 +724,7 @@ static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev)
|
|||
regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(fops);
|
||||
|
||||
static struct drm_driver atmel_hlcdc_dc_driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
|
||||
|
@ -810,31 +800,32 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
|
|||
static int atmel_hlcdc_dc_drm_suspend(struct device *dev)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(dev);
|
||||
struct drm_crtc *crtc;
|
||||
struct atmel_hlcdc_dc *dc = drm_dev->dev_private;
|
||||
struct regmap *regmap = dc->hlcdc->regmap;
|
||||
struct drm_atomic_state *state;
|
||||
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
state = drm_atomic_helper_suspend(drm_dev);
|
||||
if (IS_ERR(state))
|
||||
return PTR_ERR(state);
|
||||
|
||||
dc->suspend.state = state;
|
||||
|
||||
regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr);
|
||||
regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr);
|
||||
clk_disable_unprepare(dc->hlcdc->periph_clk);
|
||||
|
||||
drm_modeset_lock_all(drm_dev);
|
||||
list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head)
|
||||
atmel_hlcdc_crtc_suspend(crtc);
|
||||
drm_modeset_unlock_all(drm_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_dc_drm_resume(struct device *dev)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(dev);
|
||||
struct drm_crtc *crtc;
|
||||
struct atmel_hlcdc_dc *dc = drm_dev->dev_private;
|
||||
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
clk_prepare_enable(dc->hlcdc->periph_clk);
|
||||
regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr);
|
||||
|
||||
drm_modeset_lock_all(drm_dev);
|
||||
list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head)
|
||||
atmel_hlcdc_crtc_resume(crtc);
|
||||
drm_modeset_unlock_all(drm_dev);
|
||||
return 0;
|
||||
return drm_atomic_helper_resume(drm_dev, dc->suspend.state);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -358,6 +358,7 @@ struct atmel_hlcdc_plane_properties {
|
|||
* @planes: instantiated planes
|
||||
* @layers: active HLCDC layers
|
||||
* @wq: display controller workqueue
|
||||
* @suspend: used to store the HLCDC state when entering suspend
|
||||
* @commit: used for async commit handling
|
||||
*/
|
||||
struct atmel_hlcdc_dc {
|
||||
|
@ -368,6 +369,10 @@ struct atmel_hlcdc_dc {
|
|||
struct drm_crtc *crtc;
|
||||
struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
|
||||
struct workqueue_struct *wq;
|
||||
struct {
|
||||
u32 imr;
|
||||
struct drm_atomic_state *state;
|
||||
} suspend;
|
||||
struct {
|
||||
wait_queue_head_t wait;
|
||||
bool pending;
|
||||
|
@ -428,9 +433,6 @@ int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state);
|
|||
|
||||
void atmel_hlcdc_crtc_irq(struct drm_crtc *c);
|
||||
|
||||
void atmel_hlcdc_crtc_suspend(struct drm_crtc *crtc);
|
||||
void atmel_hlcdc_crtc_resume(struct drm_crtc *crtc);
|
||||
|
||||
int atmel_hlcdc_crtc_create(struct drm_device *dev);
|
||||
|
||||
int atmel_hlcdc_create_outputs(struct drm_device *dev);
|
||||
|
|
|
@ -132,6 +132,7 @@ static int bochsfb_create(struct drm_fb_helper *helper,
|
|||
info->fix.smem_start = 0;
|
||||
info->fix.smem_len = size;
|
||||
|
||||
bochs->fb.initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -148,7 +149,6 @@ static int bochs_fbdev_destroy(struct bochs_device *bochs)
|
|||
gfb->obj = NULL;
|
||||
}
|
||||
|
||||
drm_fb_helper_fini(&bochs->fb.helper);
|
||||
drm_framebuffer_unregister_private(&gfb->base);
|
||||
drm_framebuffer_cleanup(&gfb->base);
|
||||
|
||||
|
@ -180,7 +180,6 @@ int bochs_fbdev_init(struct bochs_device *bochs)
|
|||
if (ret)
|
||||
goto fini;
|
||||
|
||||
bochs->fb.initialized = true;
|
||||
return 0;
|
||||
|
||||
fini:
|
||||
|
@ -190,9 +189,9 @@ fini:
|
|||
|
||||
void bochs_fbdev_fini(struct bochs_device *bochs)
|
||||
{
|
||||
if (!bochs->fb.initialized)
|
||||
return;
|
||||
if (bochs->fb.initialized)
|
||||
bochs_fbdev_destroy(bochs);
|
||||
|
||||
bochs_fbdev_destroy(bochs);
|
||||
drm_fb_helper_fini(&bochs->fb.helper);
|
||||
bochs->fb.initialized = false;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "dw-hdmi.h"
|
||||
#include "dw-hdmi-audio.h"
|
||||
|
||||
#define DDC_SEGMENT_ADDR 0x30
|
||||
#define HDMI_EDID_LEN 512
|
||||
|
||||
#define RGB 0
|
||||
|
@ -112,6 +113,7 @@ struct dw_hdmi_i2c {
|
|||
|
||||
u8 slave_reg;
|
||||
bool is_regaddr;
|
||||
bool is_segment;
|
||||
};
|
||||
|
||||
struct dw_hdmi_phy_data {
|
||||
|
@ -247,8 +249,12 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
|
|||
reinit_completion(&i2c->cmp);
|
||||
|
||||
hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
|
||||
hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ,
|
||||
HDMI_I2CM_OPERATION);
|
||||
if (i2c->is_segment)
|
||||
hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT,
|
||||
HDMI_I2CM_OPERATION);
|
||||
else
|
||||
hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ,
|
||||
HDMI_I2CM_OPERATION);
|
||||
|
||||
stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
|
||||
if (!stat)
|
||||
|
@ -260,6 +266,7 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
|
|||
|
||||
*buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI);
|
||||
}
|
||||
i2c->is_segment = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -309,12 +316,6 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
|
|||
dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
if (msgs[i].addr != addr) {
|
||||
dev_warn(hdmi->dev,
|
||||
"unsupported transfer, changed slave address\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (msgs[i].len == 0) {
|
||||
dev_dbg(hdmi->dev,
|
||||
"unsupported transfer %d/%d, no data\n",
|
||||
|
@ -334,15 +335,24 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
|
|||
/* Set slave device register address on transfer */
|
||||
i2c->is_regaddr = false;
|
||||
|
||||
/* Set segment pointer for I2C extended read mode operation */
|
||||
i2c->is_segment = false;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
|
||||
i + 1, num, msgs[i].len, msgs[i].flags);
|
||||
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, msgs[i].len);
|
||||
else
|
||||
ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, msgs[i].len);
|
||||
|
||||
if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
|
||||
i2c->is_segment = true;
|
||||
hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR);
|
||||
hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR);
|
||||
} else {
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf,
|
||||
msgs[i].len);
|
||||
else
|
||||
ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf,
|
||||
msgs[i].len);
|
||||
}
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
@ -1230,6 +1240,58 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
|
|||
hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1);
|
||||
}
|
||||
|
||||
static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct hdmi_vendor_infoframe frame;
|
||||
u8 buffer[10];
|
||||
ssize_t err;
|
||||
|
||||
err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode);
|
||||
if (err < 0)
|
||||
/*
|
||||
* Going into that statement does not means vendor infoframe
|
||||
* fails. It just informed us that vendor infoframe is not
|
||||
* needed for the selected mode. Only 4k or stereoscopic 3D
|
||||
* mode requires vendor infoframe. So just simply return.
|
||||
*/
|
||||
return;
|
||||
|
||||
err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
|
||||
if (err < 0) {
|
||||
dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n",
|
||||
err);
|
||||
return;
|
||||
}
|
||||
hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
|
||||
HDMI_FC_DATAUTO0_VSD_MASK);
|
||||
|
||||
/* Set the length of HDMI vendor specific InfoFrame payload */
|
||||
hdmi_writeb(hdmi, buffer[2], HDMI_FC_VSDSIZE);
|
||||
|
||||
/* Set 24bit IEEE Registration Identifier */
|
||||
hdmi_writeb(hdmi, buffer[4], HDMI_FC_VSDIEEEID0);
|
||||
hdmi_writeb(hdmi, buffer[5], HDMI_FC_VSDIEEEID1);
|
||||
hdmi_writeb(hdmi, buffer[6], HDMI_FC_VSDIEEEID2);
|
||||
|
||||
/* Set HDMI_Video_Format and HDMI_VIC/3D_Structure */
|
||||
hdmi_writeb(hdmi, buffer[7], HDMI_FC_VSDPAYLOAD0);
|
||||
hdmi_writeb(hdmi, buffer[8], HDMI_FC_VSDPAYLOAD1);
|
||||
|
||||
if (frame.s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
|
||||
hdmi_writeb(hdmi, buffer[9], HDMI_FC_VSDPAYLOAD2);
|
||||
|
||||
/* Packet frame interpolation */
|
||||
hdmi_writeb(hdmi, 1, HDMI_FC_DATAUTO1);
|
||||
|
||||
/* Auto packets per frame and line spacing */
|
||||
hdmi_writeb(hdmi, 0x11, HDMI_FC_DATAUTO2);
|
||||
|
||||
/* Configures the Frame Composer On RDRB mode */
|
||||
hdmi_mask_writeb(hdmi, 1, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
|
||||
HDMI_FC_DATAUTO0_VSD_MASK);
|
||||
}
|
||||
|
||||
static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
|
@ -1479,6 +1541,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
|
|||
|
||||
/* HDMI Initialization Step F - Configure AVI InfoFrame */
|
||||
hdmi_config_AVI(hdmi, mode);
|
||||
hdmi_config_vendor_specific_infoframe(hdmi, mode);
|
||||
} else {
|
||||
dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
|
||||
}
|
||||
|
|
|
@ -854,6 +854,10 @@ enum {
|
|||
HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
|
||||
HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,
|
||||
|
||||
/* FC_DATAUTO0 field values */
|
||||
HDMI_FC_DATAUTO0_VSD_MASK = 0x08,
|
||||
HDMI_FC_DATAUTO0_VSD_OFFSET = 3,
|
||||
|
||||
/* PHY_CONF0 field values */
|
||||
HDMI_PHY_CONF0_PDZ_MASK = 0x80,
|
||||
HDMI_PHY_CONF0_PDZ_OFFSET = 7,
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_displayid.h>
|
||||
#include <drm/drm_scdc_helper.h>
|
||||
|
||||
#include "drm_crtc_internal.h"
|
||||
|
||||
|
@ -3248,6 +3249,21 @@ static bool cea_db_is_hdmi_vsdb(const u8 *db)
|
|||
return hdmi_id == HDMI_IEEE_OUI;
|
||||
}
|
||||
|
||||
static bool cea_db_is_hdmi_forum_vsdb(const u8 *db)
|
||||
{
|
||||
unsigned int oui;
|
||||
|
||||
if (cea_db_tag(db) != VENDOR_BLOCK)
|
||||
return false;
|
||||
|
||||
if (cea_db_payload_len(db) < 7)
|
||||
return false;
|
||||
|
||||
oui = db[3] << 16 | db[2] << 8 | db[1];
|
||||
|
||||
return oui == HDMI_FORUM_IEEE_OUI;
|
||||
}
|
||||
|
||||
#define for_each_cea_db(cea, i, start, end) \
|
||||
for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
|
||||
|
||||
|
@ -3799,6 +3815,48 @@ drm_default_rgb_quant_range(const struct drm_display_mode *mode)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_default_rgb_quant_range);
|
||||
|
||||
static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
|
||||
const u8 *hf_vsdb)
|
||||
{
|
||||
struct drm_display_info *display = &connector->display_info;
|
||||
struct drm_hdmi_info *hdmi = &display->hdmi;
|
||||
|
||||
if (hf_vsdb[6] & 0x80) {
|
||||
hdmi->scdc.supported = true;
|
||||
if (hf_vsdb[6] & 0x40)
|
||||
hdmi->scdc.read_request = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* All HDMI 2.0 monitors must support scrambling at rates > 340 MHz.
|
||||
* And as per the spec, three factors confirm this:
|
||||
* * Availability of a HF-VSDB block in EDID (check)
|
||||
* * Non zero Max_TMDS_Char_Rate filed in HF-VSDB (let's check)
|
||||
* * SCDC support available (let's check)
|
||||
* Lets check it out.
|
||||
*/
|
||||
|
||||
if (hf_vsdb[5]) {
|
||||
/* max clock is 5000 KHz times block value */
|
||||
u32 max_tmds_clock = hf_vsdb[5] * 5000;
|
||||
struct drm_scdc *scdc = &hdmi->scdc;
|
||||
|
||||
if (max_tmds_clock > 340000) {
|
||||
display->max_tmds_clock = max_tmds_clock;
|
||||
DRM_DEBUG_KMS("HF-VSDB: max TMDS clock %d kHz\n",
|
||||
display->max_tmds_clock);
|
||||
}
|
||||
|
||||
if (scdc->supported) {
|
||||
scdc->scrambling.supported = true;
|
||||
|
||||
/* Few sinks support scrambling for cloks < 340M */
|
||||
if ((hf_vsdb[6] & 0x8))
|
||||
scdc->scrambling.low_rates = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
|
||||
const u8 *hdmi)
|
||||
{
|
||||
|
@ -3913,6 +3971,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
|
|||
|
||||
if (cea_db_is_hdmi_vsdb(db))
|
||||
drm_parse_hdmi_vsdb_video(connector, db);
|
||||
if (cea_db_is_hdmi_forum_vsdb(db))
|
||||
drm_parse_hdmi_forum_vsdb(connector, db);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,12 +50,14 @@ DEFINE_MUTEX(drm_global_mutex);
|
|||
*
|
||||
* Drivers must define the file operations structure that forms the DRM
|
||||
* userspace API entry point, even though most of those operations are
|
||||
* implemented in the DRM core. The mandatory functions are drm_open(),
|
||||
* implemented in the DRM core. The resulting &struct file_operations must be
|
||||
* stored in the &drm_driver.fops field. The mandatory functions are drm_open(),
|
||||
* drm_read(), drm_ioctl() and drm_compat_ioctl() if CONFIG_COMPAT is enabled
|
||||
* (note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n). Drivers which
|
||||
* implement private ioctls that require 32/64 bit compatibility support must
|
||||
* provide their own .compat_ioctl() handler that processes private ioctls and
|
||||
* calls drm_compat_ioctl() for core ioctls.
|
||||
* Note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n, so there's no
|
||||
* need to sprinkle #ifdef into the code. Drivers which implement private ioctls
|
||||
* that require 32/64 bit compatibility support must provide their own
|
||||
* &file_operations.compat_ioctl handler that processes private ioctls and calls
|
||||
* drm_compat_ioctl() for core ioctls.
|
||||
*
|
||||
* In addition drm_read() and drm_poll() provide support for DRM events. DRM
|
||||
* events are a generic and extensible means to send asynchronous events to
|
||||
|
@ -63,10 +65,14 @@ DEFINE_MUTEX(drm_global_mutex);
|
|||
* page flip completions by the KMS API. But drivers can also use it for their
|
||||
* own needs, e.g. to signal completion of rendering.
|
||||
*
|
||||
* For the driver-side event interface see drm_event_reserve_init() and
|
||||
* drm_send_event() as the main starting points.
|
||||
*
|
||||
* The memory mapping implementation will vary depending on how the driver
|
||||
* manages memory. Legacy drivers will use the deprecated drm_legacy_mmap()
|
||||
* function, modern drivers should use one of the provided memory-manager
|
||||
* specific implementations. For GEM-based drivers this is drm_gem_mmap().
|
||||
* specific implementations. For GEM-based drivers this is drm_gem_mmap(), and
|
||||
* for drivers which use the CMA GEM helpers it's drm_gem_cma_mmap().
|
||||
*
|
||||
* No other file operations are supported by the DRM userspace API. Overall the
|
||||
* following is an example #file_operations structure::
|
||||
|
@ -82,6 +88,10 @@ DEFINE_MUTEX(drm_global_mutex);
|
|||
* .llseek = no_llseek,
|
||||
* .mmap = drm_gem_mmap,
|
||||
* };
|
||||
*
|
||||
* For plain GEM based drivers there is the DEFINE_DRM_GEM_FOPS() macro, and for
|
||||
* CMA based drivers there is the DEFINE_DRM_GEM_CMA_FOPS() macro to make this
|
||||
* simpler.
|
||||
*/
|
||||
|
||||
static int drm_open_helper(struct file *filp, struct drm_minor *minor);
|
||||
|
@ -111,9 +121,9 @@ static int drm_setup(struct drm_device * dev)
|
|||
* @inode: device inode
|
||||
* @filp: file pointer.
|
||||
*
|
||||
* This function must be used by drivers as their .open() #file_operations
|
||||
* method. It looks up the correct DRM device and instantiates all the per-file
|
||||
* resources for it.
|
||||
* This function must be used by drivers as their &file_operations.open method.
|
||||
* It looks up the correct DRM device and instantiates all the per-file
|
||||
* resources for it. It also calls the &drm_driver.open driver callback.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
|
@ -298,11 +308,6 @@ static void drm_events_release(struct drm_file *file_priv)
|
|||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_legacy_dev_reinit
|
||||
*
|
||||
* Reinitializes a legacy/ums drm device in it's lastclose function.
|
||||
*/
|
||||
static void drm_legacy_dev_reinit(struct drm_device *dev)
|
||||
{
|
||||
if (dev->irq_enabled)
|
||||
|
@ -327,15 +332,6 @@ static void drm_legacy_dev_reinit(struct drm_device *dev)
|
|||
DRM_DEBUG("lastclose completed\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Take down the DRM device.
|
||||
*
|
||||
* \param dev DRM device structure.
|
||||
*
|
||||
* Frees every resource in \p dev.
|
||||
*
|
||||
* \sa drm_device
|
||||
*/
|
||||
void drm_lastclose(struct drm_device * dev)
|
||||
{
|
||||
DRM_DEBUG("\n");
|
||||
|
@ -353,9 +349,11 @@ void drm_lastclose(struct drm_device * dev)
|
|||
* @inode: device inode
|
||||
* @filp: file pointer.
|
||||
*
|
||||
* This function must be used by drivers as their .release() #file_operations
|
||||
* method. It frees any resources associated with the open file, and if this is
|
||||
* the last open file for the DRM device also proceeds to call drm_lastclose().
|
||||
* This function must be used by drivers as their &file_operations.release
|
||||
* method. It frees any resources associated with the open file, and calls the
|
||||
* &drm_driver.preclose and &drm_driver.lastclose driver callbacks. If this is
|
||||
* the last open file for the DRM device also proceeds to call the
|
||||
* &drm_driver.lastclose driver callback.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
|
@ -443,13 +441,13 @@ EXPORT_SYMBOL(drm_release);
|
|||
* @count: count in bytes to read
|
||||
* @offset: offset to read
|
||||
*
|
||||
* This function must be used by drivers as their .read() #file_operations
|
||||
* This function must be used by drivers as their &file_operations.read
|
||||
* method iff they use DRM events for asynchronous signalling to userspace.
|
||||
* Since events are used by the KMS API for vblank and page flip completion this
|
||||
* means all modern display drivers must use it.
|
||||
*
|
||||
* @offset is ignore, DRM events are read like a pipe. Therefore drivers also
|
||||
* must set the .llseek() #file_operation to no_llseek(). Polling support is
|
||||
* @offset is ignored, DRM events are read like a pipe. Therefore drivers also
|
||||
* must set the &file_operation.llseek to no_llseek(). Polling support is
|
||||
* provided by drm_poll().
|
||||
*
|
||||
* This function will only ever read a full event. Therefore userspace must
|
||||
|
@ -537,10 +535,10 @@ EXPORT_SYMBOL(drm_read);
|
|||
* @filp: file pointer
|
||||
* @wait: poll waiter table
|
||||
*
|
||||
* This function must be used by drivers as their .read() #file_operations
|
||||
* method iff they use DRM events for asynchronous signalling to userspace.
|
||||
* Since events are used by the KMS API for vblank and page flip completion this
|
||||
* means all modern display drivers must use it.
|
||||
* This function must be used by drivers as their &file_operations.read method
|
||||
* iff they use DRM events for asynchronous signalling to userspace. Since
|
||||
* events are used by the KMS API for vblank and page flip completion this means
|
||||
* all modern display drivers must use it.
|
||||
*
|
||||
* See also drm_read().
|
||||
*
|
||||
|
@ -650,7 +648,8 @@ EXPORT_SYMBOL(drm_event_reserve_init);
|
|||
* @p: tracking structure for the pending event
|
||||
*
|
||||
* This function frees the event @p initialized with drm_event_reserve_init()
|
||||
* and releases any allocated space.
|
||||
* and releases any allocated space. It is used to cancel an event when the
|
||||
* nonblocking operation could not be submitted and needed to be aborted.
|
||||
*/
|
||||
void drm_event_cancel_free(struct drm_device *dev,
|
||||
struct drm_pending_event *p)
|
||||
|
|
|
@ -338,6 +338,9 @@ static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj,
|
|||
* as their ->mmap() handler in the DRM device file's file_operations
|
||||
* structure.
|
||||
*
|
||||
* Instead of directly referencing this function, drivers should use the
|
||||
* DEFINE_DRM_GEM_CMA_FOPS().macro.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*/
|
||||
|
|
|
@ -978,7 +978,7 @@ static void send_vblank_event(struct drm_device *dev,
|
|||
e->event.tv_sec = now->tv_sec;
|
||||
e->event.tv_usec = now->tv_usec;
|
||||
|
||||
trace_drm_vblank_event_delivered(e->base.pid, e->pipe,
|
||||
trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
|
||||
e->event.sequence);
|
||||
|
||||
drm_send_event_locked(dev, &e->base);
|
||||
|
@ -1198,9 +1198,9 @@ static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
|
|||
if (atomic_dec_and_test(&vblank->refcount)) {
|
||||
if (drm_vblank_offdelay == 0)
|
||||
return;
|
||||
else if (dev->vblank_disable_immediate || drm_vblank_offdelay < 0)
|
||||
else if (drm_vblank_offdelay < 0)
|
||||
vblank_disable_fn((unsigned long)vblank);
|
||||
else
|
||||
else if (!dev->vblank_disable_immediate)
|
||||
mod_timer(&vblank->disable_timer,
|
||||
jiffies + ((drm_vblank_offdelay * HZ)/1000));
|
||||
}
|
||||
|
@ -1505,7 +1505,6 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
|
|||
}
|
||||
|
||||
e->pipe = pipe;
|
||||
e->base.pid = current->pid;
|
||||
e->event.base.type = DRM_EVENT_VBLANK;
|
||||
e->event.base.length = sizeof(e->event);
|
||||
e->event.user_data = vblwait->request.signal;
|
||||
|
@ -1534,7 +1533,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
|
|||
DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
|
||||
vblwait->request.sequence, seq, pipe);
|
||||
|
||||
trace_drm_vblank_event_queued(current->pid, pipe,
|
||||
trace_drm_vblank_event_queued(file_priv, pipe,
|
||||
vblwait->request.sequence);
|
||||
|
||||
e->event.sequence = vblwait->request.sequence;
|
||||
|
@ -1611,7 +1610,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
|
|||
|
||||
ret = drm_vblank_get(dev, pipe);
|
||||
if (ret) {
|
||||
DRM_DEBUG("failed to acquire vblank counter, %d\n", ret);
|
||||
DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret);
|
||||
return ret;
|
||||
}
|
||||
seq = drm_vblank_count(dev, pipe);
|
||||
|
@ -1639,13 +1638,15 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
|
|||
return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
|
||||
}
|
||||
|
||||
DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
|
||||
vblwait->request.sequence, pipe);
|
||||
DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
|
||||
(((drm_vblank_count(dev, pipe) -
|
||||
vblwait->request.sequence) <= (1 << 23)) ||
|
||||
!vblank->enabled ||
|
||||
!dev->irq_enabled));
|
||||
if (vblwait->request.sequence != seq) {
|
||||
DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
|
||||
vblwait->request.sequence, pipe);
|
||||
DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
|
||||
(((drm_vblank_count(dev, pipe) -
|
||||
vblwait->request.sequence) <= (1 << 23)) ||
|
||||
!vblank->enabled ||
|
||||
!dev->irq_enabled));
|
||||
}
|
||||
|
||||
if (ret != -EINTR) {
|
||||
struct timeval now;
|
||||
|
@ -1654,10 +1655,10 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
|
|||
vblwait->reply.tval_sec = now.tv_sec;
|
||||
vblwait->reply.tval_usec = now.tv_usec;
|
||||
|
||||
DRM_DEBUG("returning %u to client\n",
|
||||
vblwait->reply.sequence);
|
||||
DRM_DEBUG("crtc %d returning %u to client\n",
|
||||
pipe, vblwait->reply.sequence);
|
||||
} else {
|
||||
DRM_DEBUG("vblank wait interrupted by signal\n");
|
||||
DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe);
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -1735,6 +1736,16 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
|
|||
wake_up(&vblank->queue);
|
||||
drm_handle_vblank_events(dev, pipe);
|
||||
|
||||
/* With instant-off, we defer disabling the interrupt until after
|
||||
* we finish processing the following vblank. The disable has to
|
||||
* be last (after drm_handle_vblank_events) so that the timestamp
|
||||
* is always accurate.
|
||||
*/
|
||||
if (dev->vblank_disable_immediate &&
|
||||
drm_vblank_offdelay > 0 &&
|
||||
!atomic_read(&vblank->refcount))
|
||||
vblank_disable_fn((unsigned long)vblank);
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, irqflags);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Copyright (c) 2015 NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sub license,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <drm/drm_scdc_helper.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
/**
|
||||
* DOC: scdc helpers
|
||||
*
|
||||
* Status and Control Data Channel (SCDC) is a mechanism introduced by the
|
||||
* HDMI 2.0 specification. It is a point-to-point protocol that allows the
|
||||
* HDMI source and HDMI sink to exchange data. The same I2C interface that
|
||||
* is used to access EDID serves as the transport mechanism for SCDC.
|
||||
*/
|
||||
|
||||
#define SCDC_I2C_SLAVE_ADDRESS 0x54
|
||||
|
||||
/**
|
||||
* drm_scdc_read - read a block of data from SCDC
|
||||
* @adapter: I2C controller
|
||||
* @offset: start offset of block to read
|
||||
* @buffer: return location for the block to read
|
||||
* @size: size of the block to read
|
||||
*
|
||||
* Reads a block of data from SCDC, starting at a given offset.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, negative error code on failure.
|
||||
*/
|
||||
ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
|
||||
size_t size)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_msg msgs[2] = {
|
||||
{
|
||||
.addr = SCDC_I2C_SLAVE_ADDRESS,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = &offset,
|
||||
}, {
|
||||
.addr = SCDC_I2C_SLAVE_ADDRESS,
|
||||
.flags = I2C_M_RD,
|
||||
.len = size,
|
||||
.buf = buffer,
|
||||
}
|
||||
};
|
||||
|
||||
ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != ARRAY_SIZE(msgs))
|
||||
return -EPROTO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_scdc_read);
|
||||
|
||||
/**
|
||||
* drm_scdc_write - write a block of data to SCDC
|
||||
* @adapter: I2C controller
|
||||
* @offset: start offset of block to write
|
||||
* @buffer: block of data to write
|
||||
* @size: size of the block to write
|
||||
*
|
||||
* Writes a block of data to SCDC, starting at a given offset.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, negative error code on failure.
|
||||
*/
|
||||
ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,
|
||||
const void *buffer, size_t size)
|
||||
{
|
||||
struct i2c_msg msg = {
|
||||
.addr = SCDC_I2C_SLAVE_ADDRESS,
|
||||
.flags = 0,
|
||||
.len = 1 + size,
|
||||
.buf = NULL,
|
||||
};
|
||||
void *data;
|
||||
int err;
|
||||
|
||||
data = kmalloc(1 + size, GFP_TEMPORARY);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
msg.buf = data;
|
||||
|
||||
memcpy(data, &offset, sizeof(offset));
|
||||
memcpy(data + 1, buffer, size);
|
||||
|
||||
err = i2c_transfer(adapter, &msg, 1);
|
||||
|
||||
kfree(data);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err != 1)
|
||||
return -EPROTO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_scdc_write);
|
||||
|
||||
/**
|
||||
* drm_scdc_check_scrambling_status - what is status of scrambling?
|
||||
* @adapter: I2C adapter for DDC channel
|
||||
*
|
||||
* Reads the scrambler status over SCDC, and checks the
|
||||
* scrambling status.
|
||||
*
|
||||
* Returns:
|
||||
* True if the scrambling is enabled, false otherwise.
|
||||
*/
|
||||
|
||||
bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter)
|
||||
{
|
||||
u8 status;
|
||||
int ret;
|
||||
|
||||
ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to read scrambling status, error %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
return status & SCDC_SCRAMBLING_STATUS;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_scdc_get_scrambling_status);
|
||||
|
||||
/**
|
||||
* drm_scdc_set_scrambling - enable scrambling
|
||||
* @adapter: I2C adapter for DDC channel
|
||||
* @enable: bool to indicate if scrambling is to be enabled/disabled
|
||||
*
|
||||
* Writes the TMDS config register over SCDC channel, and:
|
||||
* enables scrambling when enable = 1
|
||||
* disables scrambling when enable = 0
|
||||
*
|
||||
* Returns:
|
||||
* True if scrambling is set/reset successfully, false otherwise.
|
||||
*/
|
||||
|
||||
bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
|
||||
{
|
||||
u8 config;
|
||||
int ret;
|
||||
|
||||
ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to read tmds config, err=%d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
config |= SCDC_SCRAMBLING_ENABLE;
|
||||
else
|
||||
config &= ~SCDC_SCRAMBLING_ENABLE;
|
||||
|
||||
ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to enable scrambling, error %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_scdc_set_scrambling);
|
||||
|
||||
/**
|
||||
* drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio
|
||||
* @adapter: I2C adapter for DDC channel
|
||||
* @set: ret or reset the high clock ratio
|
||||
*
|
||||
* TMDS clock ratio calculations go like this:
|
||||
* TMDS character = 10 bit TMDS encoded value
|
||||
* TMDS character rate = The rate at which TMDS characters are transmitted(Mcsc)
|
||||
* TMDS bit rate = 10x TMDS character rate
|
||||
* As per the spec:
|
||||
* TMDS clock rate for pixel clock < 340 MHz = 1x the character rate
|
||||
* = 1/10 pixel clock rate
|
||||
* TMDS clock rate for pixel clock > 340 MHz = 0.25x the character rate
|
||||
* = 1/40 pixel clock rate
|
||||
*
|
||||
* Writes to the TMDS config register over SCDC channel, and:
|
||||
* sets TMDS clock ratio to 1/40 when set = 1
|
||||
* sets TMDS clock ratio to 1/10 when set = 0
|
||||
*
|
||||
* Returns:
|
||||
* True if write is successful, false otherwise.
|
||||
*/
|
||||
bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
|
||||
{
|
||||
u8 config;
|
||||
int ret;
|
||||
|
||||
ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to read tmds config, err=%d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (set)
|
||||
config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
|
||||
else
|
||||
config &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
|
||||
|
||||
ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to set TMDS clock ratio, error %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* The spec says that a source should wait minimum 1ms and maximum
|
||||
* 100ms after writing the TMDS config for clock ratio. Lets allow a
|
||||
* wait of upto 2ms here.
|
||||
*/
|
||||
usleep_range(1000, 2000);
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio);
|
|
@ -24,36 +24,36 @@ TRACE_EVENT(drm_vblank_event,
|
|||
);
|
||||
|
||||
TRACE_EVENT(drm_vblank_event_queued,
|
||||
TP_PROTO(pid_t pid, int crtc, unsigned int seq),
|
||||
TP_ARGS(pid, crtc, seq),
|
||||
TP_PROTO(struct drm_file *file, int crtc, unsigned int seq),
|
||||
TP_ARGS(file, crtc, seq),
|
||||
TP_STRUCT__entry(
|
||||
__field(pid_t, pid)
|
||||
__field(struct drm_file *, file)
|
||||
__field(int, crtc)
|
||||
__field(unsigned int, seq)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->pid = pid;
|
||||
__entry->file = file;
|
||||
__entry->crtc = crtc;
|
||||
__entry->seq = seq;
|
||||
),
|
||||
TP_printk("pid=%d, crtc=%d, seq=%u", __entry->pid, __entry->crtc, \
|
||||
TP_printk("file=%p, crtc=%d, seq=%u", __entry->file, __entry->crtc, \
|
||||
__entry->seq)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drm_vblank_event_delivered,
|
||||
TP_PROTO(pid_t pid, int crtc, unsigned int seq),
|
||||
TP_ARGS(pid, crtc, seq),
|
||||
TP_PROTO(struct drm_file *file, int crtc, unsigned int seq),
|
||||
TP_ARGS(file, crtc, seq),
|
||||
TP_STRUCT__entry(
|
||||
__field(pid_t, pid)
|
||||
__field(struct drm_file *, file)
|
||||
__field(int, crtc)
|
||||
__field(unsigned int, seq)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->pid = pid;
|
||||
__entry->file = file;
|
||||
__entry->crtc = crtc;
|
||||
__entry->seq = seq;
|
||||
),
|
||||
TP_printk("pid=%d, crtc=%d, seq=%u", __entry->pid, __entry->crtc, \
|
||||
TP_printk("file=%p, crtc=%d, seq=%u", __entry->file, __entry->crtc, \
|
||||
__entry->seq)
|
||||
);
|
||||
|
||||
|
|
|
@ -161,17 +161,7 @@ static void fsl_dcu_drm_lastclose(struct drm_device *dev)
|
|||
drm_fbdev_cma_restore_mode(fsl_dev->fbdev);
|
||||
}
|
||||
|
||||
static const struct file_operations fsl_dcu_drm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(fsl_dcu_drm_fops);
|
||||
|
||||
static struct drm_driver fsl_dcu_drm_driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
|
||||
|
|
|
@ -146,17 +146,7 @@ err_mode_config_cleanup:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations kirin_drm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(kirin_drm_fops);
|
||||
|
||||
static int kirin_gem_cma_dumb_create(struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
|
|
|
@ -53,16 +53,7 @@ static void imx_drm_driver_lastclose(struct drm_device *drm)
|
|||
drm_fbdev_cma_restore_mode(imxdrm->fbhelper);
|
||||
}
|
||||
|
||||
static const struct file_operations imx_drm_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(imx_drm_driver_fops);
|
||||
|
||||
void imx_drm_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
|
|
|
@ -91,19 +91,7 @@ static irqreturn_t meson_irq(int irq, void *arg)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(fops);
|
||||
|
||||
static struct drm_driver meson_driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
|
||||
|
|
|
@ -311,6 +311,8 @@ void msm_perf_debugfs_cleanup(struct msm_drm_private *priv);
|
|||
#else
|
||||
static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; }
|
||||
static inline void msm_rd_dump_submit(struct msm_gem_submit *submit) {}
|
||||
static inline void msm_rd_debugfs_cleanup(struct msm_drm_private *priv) {}
|
||||
static inline void msm_perf_debugfs_cleanup(struct msm_drm_private *priv) {}
|
||||
#endif
|
||||
|
||||
struct clk *msm_clk_get(struct platform_device *pdev, const char *name);
|
||||
|
|
|
@ -319,19 +319,7 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = noop_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(fops);
|
||||
|
||||
static struct drm_driver mxsfb_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET |
|
||||
|
|
|
@ -211,7 +211,6 @@ usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
|
|||
goto done;
|
||||
ntfy->p->base.event = &ntfy->p->e.base;
|
||||
ntfy->p->base.file_priv = f;
|
||||
ntfy->p->base.pid = current->pid;
|
||||
ntfy->p->e.base.type = DRM_NOUVEAU_EVENT_NVIF;
|
||||
ntfy->p->e.base.length = sizeof(ntfy->p->e.base) + ntfy->reply;
|
||||
|
||||
|
|
|
@ -572,6 +572,8 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane,
|
|||
ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd),
|
||||
QXL_RELEASE_CURSOR_CMD,
|
||||
&release, NULL);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
cmd = (struct qxl_cursor_cmd *) qxl_release_map(qdev, release);
|
||||
|
||||
|
|
|
@ -226,17 +226,7 @@ static void rcar_du_lastclose(struct drm_device *dev)
|
|||
drm_fbdev_cma_restore_mode(rcdu->fbdev);
|
||||
}
|
||||
|
||||
static const struct file_operations rcar_du_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops);
|
||||
|
||||
static struct drm_driver rcar_du_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME
|
||||
|
|
|
@ -94,7 +94,7 @@ static int cdn_dp_grf_write(struct cdn_dp_device *dp,
|
|||
static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
|
||||
{
|
||||
int ret;
|
||||
u32 rate;
|
||||
unsigned long rate;
|
||||
|
||||
ret = clk_prepare_enable(dp->pclk);
|
||||
if (ret < 0) {
|
||||
|
@ -123,7 +123,8 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
|
|||
|
||||
rate = clk_get_rate(dp->core_clk);
|
||||
if (!rate) {
|
||||
DRM_DEV_ERROR(dp->dev, "get clk rate failed: %d\n", rate);
|
||||
DRM_DEV_ERROR(dp->dev, "get clk rate failed\n");
|
||||
ret = -EINVAL;
|
||||
goto err_set_rate;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#define LINK_TRAINING_RETRY_MS 20
|
||||
#define LINK_TRAINING_TIMEOUT_MS 500
|
||||
|
||||
void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk)
|
||||
void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk)
|
||||
{
|
||||
writel(clk / 1000000, dp->regs + SW_CLK_H);
|
||||
}
|
||||
|
@ -671,6 +671,10 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
|
|||
rem = do_div(symbol, 1000);
|
||||
if (tu_size_reg > 64) {
|
||||
ret = -EINVAL;
|
||||
DRM_DEV_ERROR(dp->dev,
|
||||
"tu error, clk:%d, lanes:%d, rate:%d\n",
|
||||
mode->clock, dp->link.num_lanes,
|
||||
link_rate);
|
||||
goto err_config_video;
|
||||
}
|
||||
} while ((symbol <= 1) || (tu_size_reg - symbol < 4) ||
|
||||
|
|
|
@ -121,12 +121,11 @@
|
|||
|
||||
/* dptx phy addr */
|
||||
#define DP_TX_PHY_CONFIG_REG 0x2000
|
||||
#define DP_TX_PHY_STATUS_REG 0x2004
|
||||
#define DP_TX_PHY_SW_RESET 0x2008
|
||||
#define DP_TX_PHY_SCRAMBLER_SEED 0x200c
|
||||
#define DP_TX_PHY_TRAINING_01_04 0x2010
|
||||
#define DP_TX_PHY_TRAINING_05_08 0x2014
|
||||
#define DP_TX_PHY_TRAINING_09_10 0x2018
|
||||
#define DP_TX_PHY_SW_RESET 0x2004
|
||||
#define DP_TX_PHY_SCRAMBLER_SEED 0x2008
|
||||
#define DP_TX_PHY_TRAINING_01_04 0x200c
|
||||
#define DP_TX_PHY_TRAINING_05_08 0x2010
|
||||
#define DP_TX_PHY_TRAINING_09_10 0x2014
|
||||
#define TEST_COR 0x23fc
|
||||
|
||||
/* dptx hpd addr */
|
||||
|
@ -462,7 +461,7 @@ enum vic_bt_type {
|
|||
|
||||
void cdn_dp_clock_reset(struct cdn_dp_device *dp);
|
||||
|
||||
void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk);
|
||||
void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk);
|
||||
int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
|
||||
u32 i_size, const u32 *d_mem, u32 d_size);
|
||||
int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable);
|
||||
|
|
|
@ -127,17 +127,7 @@ static irqreturn_t shmob_drm_irq(int irq, void *arg)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct file_operations shmob_drm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(shmob_drm_fops);
|
||||
|
||||
static struct drm_driver shmob_drm_driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
|
||||
|
|
|
@ -167,16 +167,7 @@ static void sti_mode_config_init(struct drm_device *dev)
|
|||
dev->mode_config.funcs = &sti_mode_config_funcs;
|
||||
}
|
||||
|
||||
static const struct file_operations sti_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.release = drm_release,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(sti_driver_fops);
|
||||
|
||||
static struct drm_driver sti_driver = {
|
||||
.driver_features = DRIVER_MODESET |
|
||||
|
|
|
@ -25,17 +25,7 @@
|
|||
#include "sun4i_framebuffer.h"
|
||||
#include "sun4i_layer.h"
|
||||
|
||||
static const struct file_operations sun4i_drv_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops);
|
||||
|
||||
static struct drm_driver sun4i_drv_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
|
||||
|
|
|
@ -529,17 +529,7 @@ static int tilcdc_debugfs_init(struct drm_minor *minor)
|
|||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(fops);
|
||||
|
||||
static struct drm_driver tilcdc_driver = {
|
||||
.driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET |
|
||||
|
|
|
@ -590,7 +590,7 @@ static int mipi_dbi_spi1e_transfer(struct mipi_dbi *mipi, int dc,
|
|||
ret = spi_sync(spi, &m);
|
||||
if (ret)
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -654,7 +654,7 @@ static int mipi_dbi_spi1_transfer(struct mipi_dbi *mipi, int dc,
|
|||
ret = spi_sync(spi, &m);
|
||||
if (ret)
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,15 @@ config DRM_VC4
|
|||
tristate "Broadcom VC4 Graphics"
|
||||
depends on ARCH_BCM2835 || COMPILE_TEST
|
||||
depends on DRM
|
||||
depends on SND && SND_SOC
|
||||
depends on COMMON_CLK
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select DRM_PANEL
|
||||
select SND_PCM
|
||||
select SND_PCM_ELD
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
select DRM_MIPI_DSI
|
||||
help
|
||||
Choose this option if you have a system that has a Broadcom
|
||||
|
|
|
@ -349,26 +349,20 @@ static struct platform_driver vc4_platform_driver = {
|
|||
|
||||
static int __init vc4_drm_register(void)
|
||||
{
|
||||
int i, ret;
|
||||
int ret;
|
||||
|
||||
ret = platform_register_drivers(component_drivers,
|
||||
ARRAY_SIZE(component_drivers));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(component_drivers); i++) {
|
||||
ret = platform_driver_register(component_drivers[i]);
|
||||
if (ret) {
|
||||
while (--i >= 0)
|
||||
platform_driver_unregister(component_drivers[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return platform_driver_register(&vc4_platform_driver);
|
||||
}
|
||||
|
||||
static void __exit vc4_drm_unregister(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = ARRAY_SIZE(component_drivers) - 1; i >= 0; i--)
|
||||
platform_driver_unregister(component_drivers[i]);
|
||||
|
||||
platform_unregister_drivers(component_drivers,
|
||||
ARRAY_SIZE(component_drivers));
|
||||
platform_driver_unregister(&vc4_platform_driver);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,11 +48,27 @@
|
|||
#include "linux/clk.h"
|
||||
#include "linux/component.h"
|
||||
#include "linux/i2c.h"
|
||||
#include "linux/of_address.h"
|
||||
#include "linux/of_gpio.h"
|
||||
#include "linux/of_platform.h"
|
||||
#include "linux/rational.h"
|
||||
#include "sound/dmaengine_pcm.h"
|
||||
#include "sound/pcm_drm_eld.h"
|
||||
#include "sound/pcm_params.h"
|
||||
#include "sound/soc.h"
|
||||
#include "vc4_drv.h"
|
||||
#include "vc4_regs.h"
|
||||
|
||||
/* HDMI audio information */
|
||||
struct vc4_hdmi_audio {
|
||||
struct snd_soc_card card;
|
||||
struct snd_soc_dai_link link;
|
||||
int samplerate;
|
||||
int channels;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct snd_pcm_substream *substream;
|
||||
};
|
||||
|
||||
/* General HDMI hardware state. */
|
||||
struct vc4_hdmi {
|
||||
struct platform_device *pdev;
|
||||
|
@ -60,6 +76,8 @@ struct vc4_hdmi {
|
|||
struct drm_encoder *encoder;
|
||||
struct drm_connector *connector;
|
||||
|
||||
struct vc4_hdmi_audio audio;
|
||||
|
||||
struct i2c_adapter *ddc;
|
||||
void __iomem *hdmicore_regs;
|
||||
void __iomem *hd_regs;
|
||||
|
@ -115,6 +133,10 @@ static const struct {
|
|||
HDMI_REG(VC4_HDMI_SW_RESET_CONTROL),
|
||||
HDMI_REG(VC4_HDMI_HOTPLUG_INT),
|
||||
HDMI_REG(VC4_HDMI_HOTPLUG),
|
||||
HDMI_REG(VC4_HDMI_MAI_CHANNEL_MAP),
|
||||
HDMI_REG(VC4_HDMI_MAI_CONFIG),
|
||||
HDMI_REG(VC4_HDMI_MAI_FORMAT),
|
||||
HDMI_REG(VC4_HDMI_AUDIO_PACKET_CONFIG),
|
||||
HDMI_REG(VC4_HDMI_RAM_PACKET_CONFIG),
|
||||
HDMI_REG(VC4_HDMI_HORZA),
|
||||
HDMI_REG(VC4_HDMI_HORZB),
|
||||
|
@ -125,6 +147,7 @@ static const struct {
|
|||
HDMI_REG(VC4_HDMI_VERTB0),
|
||||
HDMI_REG(VC4_HDMI_VERTB1),
|
||||
HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL),
|
||||
HDMI_REG(VC4_HDMI_TX_PHY_CTL0),
|
||||
};
|
||||
|
||||
static const struct {
|
||||
|
@ -133,6 +156,9 @@ static const struct {
|
|||
} hd_regs[] = {
|
||||
HDMI_REG(VC4_HD_M_CTL),
|
||||
HDMI_REG(VC4_HD_MAI_CTL),
|
||||
HDMI_REG(VC4_HD_MAI_THR),
|
||||
HDMI_REG(VC4_HD_MAI_FMT),
|
||||
HDMI_REG(VC4_HD_MAI_SMP),
|
||||
HDMI_REG(VC4_HD_VID_CTL),
|
||||
HDMI_REG(VC4_HD_CSC_CTL),
|
||||
HDMI_REG(VC4_HD_FRAME_COUNT),
|
||||
|
@ -232,6 +258,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
|
|||
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
drm_edid_to_eld(connector, edid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -317,7 +344,7 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
|
|||
struct drm_device *dev = encoder->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
u32 packet_id = frame->any.type - 0x80;
|
||||
u32 packet_reg = VC4_HDMI_GCP_0 + VC4_HDMI_PACKET_STRIDE * packet_id;
|
||||
u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id);
|
||||
uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
|
||||
ssize_t len, i;
|
||||
int ret;
|
||||
|
@ -398,6 +425,24 @@ static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
|
|||
vc4_hdmi_write_infoframe(encoder, &frame);
|
||||
}
|
||||
|
||||
static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *drm = encoder->dev;
|
||||
struct vc4_dev *vc4 = drm->dev_private;
|
||||
struct vc4_hdmi *hdmi = vc4->hdmi;
|
||||
union hdmi_infoframe frame;
|
||||
int ret;
|
||||
|
||||
ret = hdmi_audio_infoframe_init(&frame.audio);
|
||||
|
||||
frame.audio.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
|
||||
frame.audio.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
|
||||
frame.audio.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
|
||||
frame.audio.channels = hdmi->audio.channels;
|
||||
|
||||
vc4_hdmi_write_infoframe(encoder, &frame);
|
||||
}
|
||||
|
||||
static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
|
||||
{
|
||||
vc4_hdmi_set_avi_infoframe(encoder);
|
||||
|
@ -606,6 +651,447 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
|
|||
.enable = vc4_hdmi_encoder_enable,
|
||||
};
|
||||
|
||||
/* HDMI audio codec callbacks */
|
||||
static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *hdmi)
|
||||
{
|
||||
struct drm_device *drm = hdmi->encoder->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
u32 hsm_clock = clk_get_rate(hdmi->hsm_clock);
|
||||
unsigned long n, m;
|
||||
|
||||
rational_best_approximation(hsm_clock, hdmi->audio.samplerate,
|
||||
VC4_HD_MAI_SMP_N_MASK >>
|
||||
VC4_HD_MAI_SMP_N_SHIFT,
|
||||
(VC4_HD_MAI_SMP_M_MASK >>
|
||||
VC4_HD_MAI_SMP_M_SHIFT) + 1,
|
||||
&n, &m);
|
||||
|
||||
HD_WRITE(VC4_HD_MAI_SMP,
|
||||
VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) |
|
||||
VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M));
|
||||
}
|
||||
|
||||
static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi)
|
||||
{
|
||||
struct drm_encoder *encoder = hdmi->encoder;
|
||||
struct drm_crtc *crtc = encoder->crtc;
|
||||
struct drm_device *drm = encoder->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
||||
u32 samplerate = hdmi->audio.samplerate;
|
||||
u32 n, cts;
|
||||
u64 tmp;
|
||||
|
||||
n = 128 * samplerate / 1000;
|
||||
tmp = (u64)(mode->clock * 1000) * n;
|
||||
do_div(tmp, 128 * samplerate);
|
||||
cts = tmp;
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_CRP_CFG,
|
||||
VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN |
|
||||
VC4_SET_FIELD(n, VC4_HDMI_CRP_CFG_N));
|
||||
|
||||
/*
|
||||
* We could get slightly more accurate clocks in some cases by
|
||||
* providing a CTS_1 value. The two CTS values are alternated
|
||||
* between based on the period fields
|
||||
*/
|
||||
HDMI_WRITE(VC4_HDMI_CTS_0, cts);
|
||||
HDMI_WRITE(VC4_HDMI_CTS_1, cts);
|
||||
}
|
||||
|
||||
static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
return snd_soc_card_get_drvdata(card);
|
||||
}
|
||||
|
||||
static int vc4_hdmi_audio_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
|
||||
struct drm_encoder *encoder = hdmi->encoder;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(encoder->dev);
|
||||
int ret;
|
||||
|
||||
if (hdmi->audio.substream && hdmi->audio.substream != substream)
|
||||
return -EINVAL;
|
||||
|
||||
hdmi->audio.substream = substream;
|
||||
|
||||
/*
|
||||
* If the HDMI encoder hasn't probed, or the encoder is
|
||||
* currently in DVI mode, treat the codec dai as missing.
|
||||
*/
|
||||
if (!encoder->crtc || !(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
|
||||
VC4_HDMI_RAM_PACKET_ENABLE))
|
||||
return -ENODEV;
|
||||
|
||||
ret = snd_pcm_hw_constraint_eld(substream->runtime,
|
||||
hdmi->connector->eld);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vc4_hdmi_audio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vc4_hdmi_audio_reset(struct vc4_hdmi *hdmi)
|
||||
{
|
||||
struct drm_encoder *encoder = hdmi->encoder;
|
||||
struct drm_device *drm = encoder->dev;
|
||||
struct device *dev = &hdmi->pdev->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
int ret;
|
||||
|
||||
ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to stop audio infoframe: %d\n", ret);
|
||||
|
||||
HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_RESET);
|
||||
HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_ERRORF);
|
||||
HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_FLUSH);
|
||||
}
|
||||
|
||||
static void vc4_hdmi_audio_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
|
||||
|
||||
if (substream != hdmi->audio.substream)
|
||||
return;
|
||||
|
||||
vc4_hdmi_audio_reset(hdmi);
|
||||
|
||||
hdmi->audio.substream = NULL;
|
||||
}
|
||||
|
||||
/* HDMI audio codec callbacks */
|
||||
static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
|
||||
struct drm_encoder *encoder = hdmi->encoder;
|
||||
struct drm_device *drm = encoder->dev;
|
||||
struct device *dev = &hdmi->pdev->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
u32 audio_packet_config, channel_mask;
|
||||
u32 channel_map, i;
|
||||
|
||||
if (substream != hdmi->audio.substream)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
|
||||
params_rate(params), params_width(params),
|
||||
params_channels(params));
|
||||
|
||||
hdmi->audio.channels = params_channels(params);
|
||||
hdmi->audio.samplerate = params_rate(params);
|
||||
|
||||
HD_WRITE(VC4_HD_MAI_CTL,
|
||||
VC4_HD_MAI_CTL_RESET |
|
||||
VC4_HD_MAI_CTL_FLUSH |
|
||||
VC4_HD_MAI_CTL_DLATE |
|
||||
VC4_HD_MAI_CTL_ERRORE |
|
||||
VC4_HD_MAI_CTL_ERRORF);
|
||||
|
||||
vc4_hdmi_audio_set_mai_clock(hdmi);
|
||||
|
||||
audio_packet_config =
|
||||
VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT |
|
||||
VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS |
|
||||
VC4_SET_FIELD(0xf, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER);
|
||||
|
||||
channel_mask = GENMASK(hdmi->audio.channels - 1, 0);
|
||||
audio_packet_config |= VC4_SET_FIELD(channel_mask,
|
||||
VC4_HDMI_AUDIO_PACKET_CEA_MASK);
|
||||
|
||||
/* Set the MAI threshold. This logic mimics the firmware's. */
|
||||
if (hdmi->audio.samplerate > 96000) {
|
||||
HD_WRITE(VC4_HD_MAI_THR,
|
||||
VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) |
|
||||
VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
|
||||
} else if (hdmi->audio.samplerate > 48000) {
|
||||
HD_WRITE(VC4_HD_MAI_THR,
|
||||
VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) |
|
||||
VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
|
||||
} else {
|
||||
HD_WRITE(VC4_HD_MAI_THR,
|
||||
VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) |
|
||||
VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) |
|
||||
VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) |
|
||||
VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW));
|
||||
}
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_MAI_CONFIG,
|
||||
VC4_HDMI_MAI_CONFIG_BIT_REVERSE |
|
||||
VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK));
|
||||
|
||||
channel_map = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (channel_mask & BIT(i))
|
||||
channel_map |= i << (3 * i);
|
||||
}
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_MAI_CHANNEL_MAP, channel_map);
|
||||
HDMI_WRITE(VC4_HDMI_AUDIO_PACKET_CONFIG, audio_packet_config);
|
||||
vc4_hdmi_set_n_cts(hdmi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vc4_hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
|
||||
struct drm_encoder *encoder = hdmi->encoder;
|
||||
struct drm_device *drm = encoder->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
vc4_hdmi_set_audio_infoframe(encoder);
|
||||
HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0,
|
||||
HDMI_READ(VC4_HDMI_TX_PHY_CTL0) &
|
||||
~VC4_HDMI_TX_PHY_RNG_PWRDN);
|
||||
HD_WRITE(VC4_HD_MAI_CTL,
|
||||
VC4_SET_FIELD(hdmi->audio.channels,
|
||||
VC4_HD_MAI_CTL_CHNUM) |
|
||||
VC4_HD_MAI_CTL_ENABLE);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
HD_WRITE(VC4_HD_MAI_CTL,
|
||||
VC4_HD_MAI_CTL_DLATE |
|
||||
VC4_HD_MAI_CTL_ERRORE |
|
||||
VC4_HD_MAI_CTL_ERRORF);
|
||||
HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0,
|
||||
HDMI_READ(VC4_HDMI_TX_PHY_CTL0) |
|
||||
VC4_HDMI_TX_PHY_RNG_PWRDN);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct vc4_hdmi *
|
||||
snd_component_to_hdmi(struct snd_soc_component *component)
|
||||
{
|
||||
struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
|
||||
|
||||
return snd_soc_card_get_drvdata(card);
|
||||
}
|
||||
|
||||
static int vc4_hdmi_audio_eld_ctl_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct vc4_hdmi *hdmi = snd_component_to_hdmi(component);
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
||||
uinfo->count = sizeof(hdmi->connector->eld);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vc4_hdmi_audio_eld_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct vc4_hdmi *hdmi = snd_component_to_hdmi(component);
|
||||
|
||||
memcpy(ucontrol->value.bytes.data, hdmi->connector->eld,
|
||||
sizeof(hdmi->connector->eld));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new vc4_hdmi_audio_controls[] = {
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "ELD",
|
||||
.info = vc4_hdmi_audio_eld_ctl_info,
|
||||
.get = vc4_hdmi_audio_eld_ctl_get,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget vc4_hdmi_audio_widgets[] = {
|
||||
SND_SOC_DAPM_OUTPUT("TX"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route vc4_hdmi_audio_routes[] = {
|
||||
{ "TX", NULL, "Playback" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_codec_driver vc4_hdmi_audio_codec_drv = {
|
||||
.component_driver = {
|
||||
.controls = vc4_hdmi_audio_controls,
|
||||
.num_controls = ARRAY_SIZE(vc4_hdmi_audio_controls),
|
||||
.dapm_widgets = vc4_hdmi_audio_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(vc4_hdmi_audio_widgets),
|
||||
.dapm_routes = vc4_hdmi_audio_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(vc4_hdmi_audio_routes),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_ops vc4_hdmi_audio_dai_ops = {
|
||||
.startup = vc4_hdmi_audio_startup,
|
||||
.shutdown = vc4_hdmi_audio_shutdown,
|
||||
.hw_params = vc4_hdmi_audio_hw_params,
|
||||
.set_fmt = vc4_hdmi_audio_set_fmt,
|
||||
.trigger = vc4_hdmi_audio_trigger,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver vc4_hdmi_audio_codec_dai_drv = {
|
||||
.name = "vc4-hdmi-hifi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
|
||||
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
|
||||
SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver vc4_hdmi_audio_cpu_dai_comp = {
|
||||
.name = "vc4-hdmi-cpu-dai-component",
|
||||
};
|
||||
|
||||
static int vc4_hdmi_audio_cpu_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &hdmi->audio.dma_data, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver vc4_hdmi_audio_cpu_dai_drv = {
|
||||
.name = "vc4-hdmi-cpu-dai",
|
||||
.probe = vc4_hdmi_audio_cpu_dai_probe,
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
|
||||
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
|
||||
SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
|
||||
},
|
||||
.ops = &vc4_hdmi_audio_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_dmaengine_pcm_config pcm_conf = {
|
||||
.chan_names[SNDRV_PCM_STREAM_PLAYBACK] = "audio-rx",
|
||||
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
|
||||
};
|
||||
|
||||
static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link = &hdmi->audio.link;
|
||||
struct snd_soc_card *card = &hdmi->audio.card;
|
||||
struct device *dev = &hdmi->pdev->dev;
|
||||
const __be32 *addr;
|
||||
int ret;
|
||||
|
||||
if (!of_find_property(dev->of_node, "dmas", NULL)) {
|
||||
dev_warn(dev,
|
||||
"'dmas' DT property is missing, no HDMI audio\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the physical address of VC4_HD_MAI_DATA. We need to retrieve
|
||||
* the bus address specified in the DT, because the physical address
|
||||
* (the one returned by platform_get_resource()) is not appropriate
|
||||
* for DMA transfers.
|
||||
* This VC/MMU should probably be exposed to avoid this kind of hacks.
|
||||
*/
|
||||
addr = of_get_address(dev->of_node, 1, NULL, NULL);
|
||||
hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA;
|
||||
hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
hdmi->audio.dma_data.maxburst = 2;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(dev, &pcm_conf, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not register PCM component: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_component(dev, &vc4_hdmi_audio_cpu_dai_comp,
|
||||
&vc4_hdmi_audio_cpu_dai_drv, 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not register CPU DAI: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register codec and codec dai */
|
||||
ret = snd_soc_register_codec(dev, &vc4_hdmi_audio_codec_drv,
|
||||
&vc4_hdmi_audio_codec_dai_drv, 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not register codec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dai_link->name = "MAI";
|
||||
dai_link->stream_name = "MAI PCM";
|
||||
dai_link->codec_dai_name = vc4_hdmi_audio_codec_dai_drv.name;
|
||||
dai_link->cpu_dai_name = dev_name(dev);
|
||||
dai_link->codec_name = dev_name(dev);
|
||||
dai_link->platform_name = dev_name(dev);
|
||||
|
||||
card->dai_link = dai_link;
|
||||
card->num_links = 1;
|
||||
card->name = "vc4-hdmi";
|
||||
card->dev = dev;
|
||||
|
||||
/*
|
||||
* Be careful, snd_soc_register_card() calls dev_set_drvdata() and
|
||||
* stores a pointer to the snd card object in dev->driver_data. This
|
||||
* means we cannot use it for something else. The hdmi back-pointer is
|
||||
* now stored in card->drvdata and should be retrieved with
|
||||
* snd_soc_card_get_drvdata() if needed.
|
||||
*/
|
||||
snd_soc_card_set_drvdata(card, hdmi);
|
||||
ret = devm_snd_soc_register_card(dev, card);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not register sound card: %d\n", ret);
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_codec:
|
||||
snd_soc_unregister_codec(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vc4_hdmi_audio_cleanup(struct vc4_hdmi *hdmi)
|
||||
{
|
||||
struct device *dev = &hdmi->pdev->dev;
|
||||
|
||||
/*
|
||||
* If drvdata is not set this means the audio card was not
|
||||
* registered, just skip codec unregistration in this case.
|
||||
*/
|
||||
if (dev_get_drvdata(dev))
|
||||
snd_soc_unregister_codec(dev);
|
||||
}
|
||||
|
||||
static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
@ -737,6 +1223,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
|
|||
goto err_destroy_encoder;
|
||||
}
|
||||
|
||||
ret = vc4_hdmi_audio_init(hdmi);
|
||||
if (ret)
|
||||
goto err_destroy_encoder;
|
||||
|
||||
return 0;
|
||||
|
||||
err_destroy_encoder:
|
||||
|
@ -758,6 +1248,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
|
|||
struct vc4_dev *vc4 = drm->dev_private;
|
||||
struct vc4_hdmi *hdmi = vc4->hdmi;
|
||||
|
||||
vc4_hdmi_audio_cleanup(hdmi);
|
||||
|
||||
vc4_hdmi_connector_destroy(hdmi->connector);
|
||||
vc4_hdmi_encoder_destroy(hdmi->encoder);
|
||||
|
||||
|
|
|
@ -842,10 +842,8 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
|
|||
|
||||
vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
|
||||
GFP_KERNEL);
|
||||
if (!vc4_plane) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
if (!vc4_plane)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
|
||||
/* Don't allow YUV in cursor planes, since that means
|
||||
|
@ -866,9 +864,4 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
|
|||
drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
|
||||
|
||||
return plane;
|
||||
fail:
|
||||
if (plane)
|
||||
vc4_plane_destroy(plane);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
|
|
@ -446,11 +446,62 @@
|
|||
#define VC4_HDMI_HOTPLUG 0x00c
|
||||
# define VC4_HDMI_HOTPLUG_CONNECTED BIT(0)
|
||||
|
||||
/* 3 bits per field, where each field maps from that corresponding MAI
|
||||
* bus channel to the given HDMI channel.
|
||||
*/
|
||||
#define VC4_HDMI_MAI_CHANNEL_MAP 0x090
|
||||
|
||||
#define VC4_HDMI_MAI_CONFIG 0x094
|
||||
# define VC4_HDMI_MAI_CONFIG_FORMAT_REVERSE BIT(27)
|
||||
# define VC4_HDMI_MAI_CONFIG_BIT_REVERSE BIT(26)
|
||||
# define VC4_HDMI_MAI_CHANNEL_MASK_MASK VC4_MASK(15, 0)
|
||||
# define VC4_HDMI_MAI_CHANNEL_MASK_SHIFT 0
|
||||
|
||||
/* Last received format word on the MAI bus. */
|
||||
#define VC4_HDMI_MAI_FORMAT 0x098
|
||||
|
||||
#define VC4_HDMI_AUDIO_PACKET_CONFIG 0x09c
|
||||
# define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT BIT(29)
|
||||
# define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS BIT(24)
|
||||
# define VC4_HDMI_AUDIO_PACKET_FORCE_SAMPLE_PRESENT BIT(19)
|
||||
# define VC4_HDMI_AUDIO_PACKET_FORCE_B_FRAME BIT(18)
|
||||
# define VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER_MASK VC4_MASK(13, 10)
|
||||
# define VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER_SHIFT 10
|
||||
/* If set, then multichannel, otherwise 2 channel. */
|
||||
# define VC4_HDMI_AUDIO_PACKET_AUDIO_LAYOUT BIT(9)
|
||||
/* If set, then AUDIO_LAYOUT overrides audio_cea_mask */
|
||||
# define VC4_HDMI_AUDIO_PACKET_FORCE_AUDIO_LAYOUT BIT(8)
|
||||
# define VC4_HDMI_AUDIO_PACKET_CEA_MASK_MASK VC4_MASK(7, 0)
|
||||
# define VC4_HDMI_AUDIO_PACKET_CEA_MASK_SHIFT 0
|
||||
|
||||
#define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0
|
||||
# define VC4_HDMI_RAM_PACKET_ENABLE BIT(16)
|
||||
|
||||
#define VC4_HDMI_RAM_PACKET_STATUS 0x0a4
|
||||
|
||||
#define VC4_HDMI_CRP_CFG 0x0a8
|
||||
/* When set, the CTS_PERIOD counts based on MAI bus sync pulse instead
|
||||
* of pixel clock.
|
||||
*/
|
||||
# define VC4_HDMI_CRP_USE_MAI_BUS_SYNC_FOR_CTS BIT(26)
|
||||
/* When set, no CRP packets will be sent. */
|
||||
# define VC4_HDMI_CRP_CFG_DISABLE BIT(25)
|
||||
/* If set, generates CTS values based on N, audio clock, and video
|
||||
* clock. N must be divisible by 128.
|
||||
*/
|
||||
# define VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN BIT(24)
|
||||
# define VC4_HDMI_CRP_CFG_N_MASK VC4_MASK(19, 0)
|
||||
# define VC4_HDMI_CRP_CFG_N_SHIFT 0
|
||||
|
||||
/* 20-bit fields containing CTS values to be transmitted if !EXTERNAL_CTS_EN */
|
||||
#define VC4_HDMI_CTS_0 0x0ac
|
||||
#define VC4_HDMI_CTS_1 0x0b0
|
||||
/* 20-bit fields containing number of clocks to send CTS0/1 before
|
||||
* switching to the other one.
|
||||
*/
|
||||
#define VC4_HDMI_CTS_PERIOD_0 0x0b4
|
||||
#define VC4_HDMI_CTS_PERIOD_1 0x0b8
|
||||
|
||||
#define VC4_HDMI_HORZA 0x0c4
|
||||
# define VC4_HDMI_HORZA_VPOS BIT(14)
|
||||
# define VC4_HDMI_HORZA_HPOS BIT(13)
|
||||
|
@ -512,7 +563,11 @@
|
|||
|
||||
#define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0
|
||||
|
||||
#define VC4_HDMI_GCP_0 0x400
|
||||
#define VC4_HDMI_TX_PHY_CTL0 0x2c4
|
||||
# define VC4_HDMI_TX_PHY_RNG_PWRDN BIT(25)
|
||||
|
||||
#define VC4_HDMI_GCP(x) (0x400 + ((x) * 0x4))
|
||||
#define VC4_HDMI_RAM_PACKET(x) (0x400 + ((x) * 0x24))
|
||||
#define VC4_HDMI_PACKET_STRIDE 0x24
|
||||
|
||||
#define VC4_HD_M_CTL 0x00c
|
||||
|
@ -522,6 +577,56 @@
|
|||
# define VC4_HD_M_ENABLE BIT(0)
|
||||
|
||||
#define VC4_HD_MAI_CTL 0x014
|
||||
/* Set when audio stream is received at a slower rate than the
|
||||
* sampling period, so MAI fifo goes empty. Write 1 to clear.
|
||||
*/
|
||||
# define VC4_HD_MAI_CTL_DLATE BIT(15)
|
||||
# define VC4_HD_MAI_CTL_BUSY BIT(14)
|
||||
# define VC4_HD_MAI_CTL_CHALIGN BIT(13)
|
||||
# define VC4_HD_MAI_CTL_WHOLSMP BIT(12)
|
||||
# define VC4_HD_MAI_CTL_FULL BIT(11)
|
||||
# define VC4_HD_MAI_CTL_EMPTY BIT(10)
|
||||
# define VC4_HD_MAI_CTL_FLUSH BIT(9)
|
||||
/* If set, MAI bus generates SPDIF (bit 31) parity instead of passing
|
||||
* through.
|
||||
*/
|
||||
# define VC4_HD_MAI_CTL_PAREN BIT(8)
|
||||
# define VC4_HD_MAI_CTL_CHNUM_MASK VC4_MASK(7, 4)
|
||||
# define VC4_HD_MAI_CTL_CHNUM_SHIFT 4
|
||||
# define VC4_HD_MAI_CTL_ENABLE BIT(3)
|
||||
/* Underflow error status bit, write 1 to clear. */
|
||||
# define VC4_HD_MAI_CTL_ERRORE BIT(2)
|
||||
/* Overflow error status bit, write 1 to clear. */
|
||||
# define VC4_HD_MAI_CTL_ERRORF BIT(1)
|
||||
/* Single-shot reset bit. Read value is undefined. */
|
||||
# define VC4_HD_MAI_CTL_RESET BIT(0)
|
||||
|
||||
#define VC4_HD_MAI_THR 0x018
|
||||
# define VC4_HD_MAI_THR_PANICHIGH_MASK VC4_MASK(29, 24)
|
||||
# define VC4_HD_MAI_THR_PANICHIGH_SHIFT 24
|
||||
# define VC4_HD_MAI_THR_PANICLOW_MASK VC4_MASK(21, 16)
|
||||
# define VC4_HD_MAI_THR_PANICLOW_SHIFT 16
|
||||
# define VC4_HD_MAI_THR_DREQHIGH_MASK VC4_MASK(13, 8)
|
||||
# define VC4_HD_MAI_THR_DREQHIGH_SHIFT 8
|
||||
# define VC4_HD_MAI_THR_DREQLOW_MASK VC4_MASK(5, 0)
|
||||
# define VC4_HD_MAI_THR_DREQLOW_SHIFT 0
|
||||
|
||||
/* Format header to be placed on the MAI data. Unused. */
|
||||
#define VC4_HD_MAI_FMT 0x01c
|
||||
|
||||
/* Register for DMAing in audio data to be transported over the MAI
|
||||
* bus to the Falcon core.
|
||||
*/
|
||||
#define VC4_HD_MAI_DATA 0x020
|
||||
|
||||
/* Divider from HDMI HSM clock to MAI serial clock. Sampling period
|
||||
* converges to N / (M + 1) cycles.
|
||||
*/
|
||||
#define VC4_HD_MAI_SMP 0x02c
|
||||
# define VC4_HD_MAI_SMP_N_MASK VC4_MASK(31, 8)
|
||||
# define VC4_HD_MAI_SMP_N_SHIFT 8
|
||||
# define VC4_HD_MAI_SMP_M_MASK VC4_MASK(7, 0)
|
||||
# define VC4_HD_MAI_SMP_M_SHIFT 0
|
||||
|
||||
#define VC4_HD_VID_CTL 0x038
|
||||
# define VC4_HD_VID_CTL_ENABLE BIT(31)
|
||||
|
|
|
@ -104,7 +104,7 @@ static int vgem_open(struct drm_device *dev, struct drm_file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void vgem_preclose(struct drm_device *dev, struct drm_file *file)
|
||||
static void vgem_postclose(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct vgem_file *vfile = file->driver_priv;
|
||||
|
||||
|
@ -303,7 +303,7 @@ static int vgem_prime_mmap(struct drm_gem_object *obj,
|
|||
static struct drm_driver vgem_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_PRIME,
|
||||
.open = vgem_open,
|
||||
.preclose = vgem_preclose,
|
||||
.postclose = vgem_postclose,
|
||||
.gem_free_object_unlocked = vgem_gem_free_object,
|
||||
.gem_vm_ops = &vgem_gem_vm_ops,
|
||||
.ioctls = vgem_ioctls,
|
||||
|
|
|
@ -97,8 +97,8 @@ virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev,
|
|||
struct virtio_gpu_vbuffer *vbuf;
|
||||
|
||||
vbuf = kmem_cache_alloc(vgdev->vbufs, GFP_KERNEL);
|
||||
if (IS_ERR(vbuf))
|
||||
return ERR_CAST(vbuf);
|
||||
if (!vbuf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
memset(vbuf, 0, VBUFFER_SIZE);
|
||||
|
||||
BUG_ON(size > MAX_INLINE_CMD_SIZE);
|
||||
|
|
|
@ -53,19 +53,7 @@ static void zx_drm_lastclose(struct drm_device *drm)
|
|||
drm_fbdev_cma_restore_mode(priv->fbdev);
|
||||
}
|
||||
|
||||
static const struct file_operations zx_drm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = noop_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_CMA_FOPS(zx_drm_fops);
|
||||
|
||||
static struct drm_driver zx_drm_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
|
||||
|
|
|
@ -87,6 +87,53 @@ enum subpixel_order {
|
|||
SubPixelVerticalRGB,
|
||||
SubPixelVerticalBGR,
|
||||
SubPixelNone,
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_scrambling: sink's scrambling support.
|
||||
*/
|
||||
struct drm_scrambling {
|
||||
/**
|
||||
* @supported: scrambling supported for rates > 340 Mhz.
|
||||
*/
|
||||
bool supported;
|
||||
/**
|
||||
* @low_rates: scrambling supported for rates <= 340 Mhz.
|
||||
*/
|
||||
bool low_rates;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct drm_scdc - Information about scdc capabilities of a HDMI 2.0 sink
|
||||
*
|
||||
* Provides SCDC register support and capabilities related information on a
|
||||
* HDMI 2.0 sink. In case of a HDMI 1.4 sink, all parameter must be 0.
|
||||
*/
|
||||
struct drm_scdc {
|
||||
/**
|
||||
* @supported: status control & data channel present.
|
||||
*/
|
||||
bool supported;
|
||||
/**
|
||||
* @read_request: sink is capable of generating scdc read request.
|
||||
*/
|
||||
bool read_request;
|
||||
/**
|
||||
* @scrambling: sink's scrambling capabilities
|
||||
*/
|
||||
struct drm_scrambling scrambling;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct drm_hdmi_info - runtime information about the connected HDMI sink
|
||||
*
|
||||
* Describes if a given display supports advanced HDMI 2.0 features.
|
||||
* This information is available in CEA-861-F extension blocks (like HF-VSDB).
|
||||
*/
|
||||
struct drm_hdmi_info {
|
||||
struct drm_scdc scdc;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -204,6 +251,11 @@ struct drm_display_info {
|
|||
* @cea_rev: CEA revision of the HDMI sink.
|
||||
*/
|
||||
u8 cea_rev;
|
||||
|
||||
/**
|
||||
* @hdmi: advance features of a HDMI sink.
|
||||
*/
|
||||
struct drm_hdmi_info hdmi;
|
||||
};
|
||||
|
||||
int drm_display_info_set_bus_formats(struct drm_display_info *info,
|
||||
|
|
|
@ -64,7 +64,6 @@ struct drm_mode_create_dumb;
|
|||
* structure for GEM drivers.
|
||||
*/
|
||||
struct drm_driver {
|
||||
|
||||
/**
|
||||
* @load:
|
||||
*
|
||||
|
@ -76,14 +75,94 @@ struct drm_driver {
|
|||
* See drm_dev_init() and drm_dev_register() for proper and
|
||||
* race-free way to set up a &struct drm_device.
|
||||
*
|
||||
* This is deprecated, do not use!
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* Zero on success, non-zero value on failure.
|
||||
*/
|
||||
int (*load) (struct drm_device *, unsigned long flags);
|
||||
|
||||
/**
|
||||
* @open:
|
||||
*
|
||||
* Driver callback when a new &struct drm_file is opened. Useful for
|
||||
* setting up driver-private data structures like buffer allocators,
|
||||
* execution contexts or similar things. Such driver-private resources
|
||||
* must be released again in @postclose.
|
||||
*
|
||||
* Since the display/modeset side of DRM can only be owned by exactly
|
||||
* one &struct drm_file (see &drm_file.is_master and &drm_device.master)
|
||||
* there should never be a need to set up any modeset related resources
|
||||
* in this callback. Doing so would be a driver design bug.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* 0 on success, a negative error code on failure, which will be
|
||||
* promoted to userspace as the result of the open() system call.
|
||||
*/
|
||||
int (*open) (struct drm_device *, struct drm_file *);
|
||||
|
||||
/**
|
||||
* @preclose:
|
||||
*
|
||||
* One of the driver callbacks when a new &struct drm_file is closed.
|
||||
* Useful for tearing down driver-private data structures allocated in
|
||||
* @open like buffer allocators, execution contexts or similar things.
|
||||
*
|
||||
* Since the display/modeset side of DRM can only be owned by exactly
|
||||
* one &struct drm_file (see &drm_file.is_master and &drm_device.master)
|
||||
* there should never be a need to tear down any modeset related
|
||||
* resources in this callback. Doing so would be a driver design bug.
|
||||
*
|
||||
* FIXME: It is not really clear why there's both @preclose and
|
||||
* @postclose. Without a really good reason, use @postclose only.
|
||||
*/
|
||||
void (*preclose) (struct drm_device *, struct drm_file *file_priv);
|
||||
|
||||
/**
|
||||
* @postclose:
|
||||
*
|
||||
* One of the driver callbacks when a new &struct drm_file is closed.
|
||||
* Useful for tearing down driver-private data structures allocated in
|
||||
* @open like buffer allocators, execution contexts or similar things.
|
||||
*
|
||||
* Since the display/modeset side of DRM can only be owned by exactly
|
||||
* one &struct drm_file (see &drm_file.is_master and &drm_device.master)
|
||||
* there should never be a need to tear down any modeset related
|
||||
* resources in this callback. Doing so would be a driver design bug.
|
||||
*
|
||||
* FIXME: It is not really clear why there's both @preclose and
|
||||
* @postclose. Without a really good reason, use @postclose only.
|
||||
*/
|
||||
void (*postclose) (struct drm_device *, struct drm_file *);
|
||||
|
||||
/**
|
||||
* @lastclose:
|
||||
*
|
||||
* Called when the last &struct drm_file has been closed and there's
|
||||
* currently no userspace client for the &struct drm_device.
|
||||
*
|
||||
* Modern drivers should only use this to force-restore the fbdev
|
||||
* framebuffer using drm_fb_helper_restore_fbdev_mode_unlocked().
|
||||
* Anything else would indicate there's something seriously wrong.
|
||||
* Modern drivers can also use this to execute delayed power switching
|
||||
* state changes, e.g. in conjunction with the :ref:`vga_switcheroo`
|
||||
* infrastructure.
|
||||
*
|
||||
* This is called after @preclose and @postclose have been called.
|
||||
*
|
||||
* NOTE:
|
||||
*
|
||||
* All legacy drivers use this callback to de-initialize the hardware.
|
||||
* This is purely because of the shadow-attach model, where the DRM
|
||||
* kernel driver does not really own the hardware. Instead ownershipe is
|
||||
* handled with the help of userspace through an inheritedly racy dance
|
||||
* to set/unset the VT into raw mode.
|
||||
*
|
||||
* Legacy drivers initialize the hardware in the @firstopen callback,
|
||||
* which isn't even called for modern drivers.
|
||||
*/
|
||||
void (*lastclose) (struct drm_device *);
|
||||
|
||||
/**
|
||||
|
|
|
@ -476,5 +476,4 @@ void drm_edid_get_monitor_name(struct edid *edid, char *name,
|
|||
struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
|
||||
int hsize, int vsize, int fresh,
|
||||
bool rb);
|
||||
|
||||
#endif /* __DRM_EDID_H__ */
|
||||
|
|
|
@ -45,6 +45,7 @@ struct drm_device;
|
|||
* FIXME: Not sure we want to have drm_minor here in the end, but to avoid
|
||||
* header include loops we need it here for now.
|
||||
*/
|
||||
|
||||
enum drm_minor_type {
|
||||
DRM_MINOR_PRIMARY,
|
||||
DRM_MINOR_CONTROL,
|
||||
|
@ -52,12 +53,19 @@ enum drm_minor_type {
|
|||
};
|
||||
|
||||
/**
|
||||
* DRM minor structure. This structure represents a drm minor number.
|
||||
* struct drm_minor - DRM device minor structure
|
||||
*
|
||||
* This structure represents a DRM minor number for device nodes in /dev.
|
||||
* Entirely opaque to drivers and should never be inspected directly by drivers.
|
||||
* Drivers instead should only interact with &struct drm_file and of course
|
||||
* &struct drm_device, which is also where driver-private data and resources can
|
||||
* be attached to.
|
||||
*/
|
||||
struct drm_minor {
|
||||
int index; /**< Minor device number */
|
||||
int type; /**< Control or render */
|
||||
struct device *kdev; /**< Linux device */
|
||||
/* private: */
|
||||
int index; /* Minor device number */
|
||||
int type; /* Control or render */
|
||||
struct device *kdev; /* Linux device */
|
||||
struct drm_device *dev;
|
||||
|
||||
struct dentry *debugfs_root;
|
||||
|
@ -66,91 +74,286 @@ struct drm_minor {
|
|||
struct mutex debugfs_lock; /* Protects debugfs_list. */
|
||||
};
|
||||
|
||||
/* Event queued up for userspace to read */
|
||||
/**
|
||||
* struct drm_pending_event - Event queued up for userspace to read
|
||||
*
|
||||
* This represents a DRM event. Drivers can use this as a generic completion
|
||||
* mechanism, which supports kernel-internal &struct completion, &struct dma_fence
|
||||
* and also the DRM-specific &struct drm_event delivery mechanism.
|
||||
*/
|
||||
struct drm_pending_event {
|
||||
/**
|
||||
* @completion:
|
||||
*
|
||||
* Optional pointer to a kernel internal completion signalled when
|
||||
* drm_send_event() is called, useful to internally synchronize with
|
||||
* nonblocking operations.
|
||||
*/
|
||||
struct completion *completion;
|
||||
|
||||
/**
|
||||
* @completion_release:
|
||||
*
|
||||
* Optional callback currently only used by the atomic modeset helpers
|
||||
* to clean up the reference count for the structure @completion is
|
||||
* stored in.
|
||||
*/
|
||||
void (*completion_release)(struct completion *completion);
|
||||
|
||||
/**
|
||||
* @event:
|
||||
*
|
||||
* Pointer to the actual event that should be sent to userspace to be
|
||||
* read using drm_read(). Can be optional, since nowadays events are
|
||||
* also used to signal kernel internal threads with @completion or DMA
|
||||
* transactions using @fence.
|
||||
*/
|
||||
struct drm_event *event;
|
||||
|
||||
/**
|
||||
* @fence:
|
||||
*
|
||||
* Optional DMA fence to unblock other hardware transactions which
|
||||
* depend upon the nonblocking DRM operation this event represents.
|
||||
*/
|
||||
struct dma_fence *fence;
|
||||
struct list_head link;
|
||||
struct list_head pending_link;
|
||||
|
||||
/**
|
||||
* @file_priv:
|
||||
*
|
||||
* &struct drm_file where @event should be delivered to. Only set when
|
||||
* @event is set.
|
||||
*/
|
||||
struct drm_file *file_priv;
|
||||
pid_t pid; /* pid of requester, no guarantee it's valid by the time
|
||||
we deliver the event, for tracing only */
|
||||
|
||||
/**
|
||||
* @link:
|
||||
*
|
||||
* Double-linked list to keep track of this event. Can be used by the
|
||||
* driver up to the point when it calls drm_send_event(), after that
|
||||
* this list entry is owned by the core for its own book-keeping.
|
||||
*/
|
||||
struct list_head link;
|
||||
|
||||
/**
|
||||
* @pending_link:
|
||||
*
|
||||
* Entry on &drm_file.pending_event_list, to keep track of all pending
|
||||
* events for @file_priv, to allow correct unwinding of them when
|
||||
* userspace closes the file before the event is delivered.
|
||||
*/
|
||||
struct list_head pending_link;
|
||||
};
|
||||
|
||||
/** File private data */
|
||||
/**
|
||||
* struct drm_file - DRM file private data
|
||||
*
|
||||
* This structure tracks DRM state per open file descriptor.
|
||||
*/
|
||||
struct drm_file {
|
||||
/**
|
||||
* @authenticated:
|
||||
*
|
||||
* Whether the client is allowed to submit rendering, which for legacy
|
||||
* nodes means it must be authenticated.
|
||||
*
|
||||
* See also the :ref:`section on primary nodes and authentication
|
||||
* <drm_primary_node>`.
|
||||
*/
|
||||
unsigned authenticated :1;
|
||||
/* true when the client has asked us to expose stereo 3D mode flags */
|
||||
|
||||
/**
|
||||
* @stereo_allowed:
|
||||
*
|
||||
* True when the client has asked us to expose stereo 3D mode flags.
|
||||
*/
|
||||
unsigned stereo_allowed :1;
|
||||
/*
|
||||
* true if client understands CRTC primary planes and cursor planes
|
||||
* in the plane list
|
||||
|
||||
/**
|
||||
* @universal_planes:
|
||||
*
|
||||
* True if client understands CRTC primary planes and cursor planes
|
||||
* in the plane list. Automatically set when @atomic is set.
|
||||
*/
|
||||
unsigned universal_planes:1;
|
||||
/* true if client understands atomic properties */
|
||||
|
||||
/** @atomic: True if client understands atomic properties. */
|
||||
unsigned atomic:1;
|
||||
/*
|
||||
* This client is the creator of @master.
|
||||
* Protected by struct drm_device::master_mutex.
|
||||
|
||||
/**
|
||||
* @is_master:
|
||||
*
|
||||
* This client is the creator of @master. Protected by struct
|
||||
* &drm_device.master_mutex.
|
||||
*
|
||||
* See also the :ref:`section on primary nodes and authentication
|
||||
* <drm_primary_node>`.
|
||||
*/
|
||||
unsigned is_master:1;
|
||||
|
||||
struct pid *pid;
|
||||
drm_magic_t magic;
|
||||
struct list_head lhead;
|
||||
struct drm_minor *minor;
|
||||
unsigned long lock_count;
|
||||
/**
|
||||
* @master:
|
||||
*
|
||||
* Master this node is currently associated with. Only relevant if
|
||||
* drm_is_primary_client() returns true. Note that this only
|
||||
* matches &drm_device.master if the master is the currently active one.
|
||||
*
|
||||
* See also @authentication and @is_master and the :ref:`section on
|
||||
* primary nodes and authentication <drm_primary_node>`.
|
||||
*/
|
||||
struct drm_master *master;
|
||||
|
||||
/** Mapping of mm object handles to object pointers. */
|
||||
/** @pid: Process that opened this file. */
|
||||
struct pid *pid;
|
||||
|
||||
/** @magic: Authentication magic, see @authenticated. */
|
||||
drm_magic_t magic;
|
||||
|
||||
/**
|
||||
* @lhead:
|
||||
*
|
||||
* List of all open files of a DRM device, linked into
|
||||
* &drm_device.filelist. Protected by &drm_device.filelist_mutex.
|
||||
*/
|
||||
struct list_head lhead;
|
||||
|
||||
/** @minor: &struct drm_minor for this file. */
|
||||
struct drm_minor *minor;
|
||||
|
||||
/**
|
||||
* @object_idr:
|
||||
*
|
||||
* Mapping of mm object handles to object pointers. Used by the GEM
|
||||
* subsystem. Protected by @table_lock.
|
||||
*/
|
||||
struct idr object_idr;
|
||||
/** Lock for synchronization of access to object_idr. */
|
||||
|
||||
/** @table_lock: Protects @object_idr. */
|
||||
spinlock_t table_lock;
|
||||
|
||||
/** @filp: Pointer to the core file structure. */
|
||||
struct file *filp;
|
||||
|
||||
/**
|
||||
* @driver_priv:
|
||||
*
|
||||
* Optional pointer for driver private data. Can be allocated in
|
||||
* &drm_driver.open and should be freed in &drm_driver.postclose.
|
||||
*/
|
||||
void *driver_priv;
|
||||
|
||||
struct drm_master *master; /* master this node is currently associated with
|
||||
N.B. not always dev->master */
|
||||
/**
|
||||
* fbs - List of framebuffers associated with this file.
|
||||
* @fbs:
|
||||
*
|
||||
* Protected by fbs_lock. Note that the fbs list holds a reference on
|
||||
* the fb object to prevent it from untimely disappearing.
|
||||
* List of &struct drm_framebuffer associated with this file, using the
|
||||
* &drm_framebuffer.filp_head entry.
|
||||
*
|
||||
* Protected by @fbs_lock. Note that the @fbs list holds a reference on
|
||||
* the framebuffer object to prevent it from untimely disappearing.
|
||||
*/
|
||||
struct list_head fbs;
|
||||
|
||||
/** @fbs_lock: Protects @fbs. */
|
||||
struct mutex fbs_lock;
|
||||
|
||||
/** User-created blob properties; this retains a reference on the
|
||||
* property. */
|
||||
/**
|
||||
* @blobs:
|
||||
*
|
||||
* User-created blob properties; this retains a reference on the
|
||||
* property.
|
||||
*
|
||||
* Protected by @drm_mode_config.blob_lock;
|
||||
*/
|
||||
struct list_head blobs;
|
||||
|
||||
/** @event_wait: Waitqueue for new events added to @event_list. */
|
||||
wait_queue_head_t event_wait;
|
||||
|
||||
/**
|
||||
* @pending_event_list:
|
||||
*
|
||||
* List of pending &struct drm_pending_event, used to clean up pending
|
||||
* events in case this file gets closed before the event is signalled.
|
||||
* Uses the &drm_pending_event.pending_link entry.
|
||||
*
|
||||
* Protect by &drm_device.event_lock.
|
||||
*/
|
||||
struct list_head pending_event_list;
|
||||
|
||||
/**
|
||||
* @event_list:
|
||||
*
|
||||
* List of &struct drm_pending_event, ready for delivery to userspace
|
||||
* through drm_read(). Uses the &drm_pending_event.link entry.
|
||||
*
|
||||
* Protect by &drm_device.event_lock.
|
||||
*/
|
||||
struct list_head event_list;
|
||||
|
||||
/**
|
||||
* @event_space:
|
||||
*
|
||||
* Available event space to prevent userspace from
|
||||
* exhausting kernel memory. Currently limited to the fairly arbitrary
|
||||
* value of 4KB.
|
||||
*/
|
||||
int event_space;
|
||||
|
||||
/** @event_read_lock: Serializes drm_read(). */
|
||||
struct mutex event_read_lock;
|
||||
|
||||
/**
|
||||
* @prime:
|
||||
*
|
||||
* Per-file buffer caches used by the PRIME buffer sharing code.
|
||||
*/
|
||||
struct drm_prime_file_private prime;
|
||||
|
||||
/* private: */
|
||||
unsigned long lock_count; /* DRI1 legacy lock count */
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_is_primary_client - is this an open file of the primary node
|
||||
* @file_priv: DRM file
|
||||
*
|
||||
* Returns true if this is an open file of the primary node, i.e.
|
||||
* &drm_file.minor of @file_priv is a primary minor.
|
||||
*
|
||||
* See also the :ref:`section on primary nodes and authentication
|
||||
* <drm_primary_node>`.
|
||||
*/
|
||||
static inline bool drm_is_primary_client(const struct drm_file *file_priv)
|
||||
{
|
||||
return file_priv->minor->type == DRM_MINOR_PRIMARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_is_render_client - is this an open file of the render node
|
||||
* @file_priv: DRM file
|
||||
*
|
||||
* Returns true if this is an open file of the render node, i.e.
|
||||
* &drm_file.minor of @file_priv is a render minor.
|
||||
*
|
||||
* See also the :ref:`section on render nodes <drm_render_node>`.
|
||||
*/
|
||||
static inline bool drm_is_render_client(const struct drm_file *file_priv)
|
||||
{
|
||||
return file_priv->minor->type == DRM_MINOR_RENDER;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_is_control_client - is this an open file of the control node
|
||||
* @file_priv: DRM file
|
||||
*
|
||||
* Control nodes are deprecated and in the process of getting removed from the
|
||||
* DRM userspace API. Do not ever use!
|
||||
*/
|
||||
static inline bool drm_is_control_client(const struct drm_file *file_priv)
|
||||
{
|
||||
return file_priv->minor->type == DRM_MINOR_CONTROL;
|
||||
}
|
||||
|
||||
static inline bool drm_is_primary_client(const struct drm_file *file_priv)
|
||||
{
|
||||
return file_priv->minor->type == DRM_MINOR_PRIMARY;
|
||||
}
|
||||
|
||||
int drm_open(struct inode *inode, struct file *filp);
|
||||
ssize_t drm_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *offset);
|
||||
|
|
|
@ -178,6 +178,32 @@ struct drm_gem_object {
|
|||
struct dma_buf_attachment *import_attach;
|
||||
};
|
||||
|
||||
/**
|
||||
* DEFINE_DRM_GEM_FOPS() - macro to generate file operations for GEM drivers
|
||||
* @name: name for the generated structure
|
||||
*
|
||||
* This macro autogenerates a suitable &struct file_operations for GEM based
|
||||
* drivers, which can be assigned to &drm_driver.fops. Note that this structure
|
||||
* cannot be shared between drivers, because it contains a reference to the
|
||||
* current module using THIS_MODULE.
|
||||
*
|
||||
* Note that the declaration is already marked as static - if you need a
|
||||
* non-static version of this you're probably doing it wrong and will break the
|
||||
* THIS_MODULE reference by accident.
|
||||
*/
|
||||
#define DEFINE_DRM_GEM_FOPS(name) \
|
||||
static const struct file_operations name = {\
|
||||
.owner = THIS_MODULE,\
|
||||
.open = drm_open,\
|
||||
.release = drm_release,\
|
||||
.unlocked_ioctl = drm_ioctl,\
|
||||
.compat_ioctl = drm_compat_ioctl,\
|
||||
.poll = drm_poll,\
|
||||
.read = drm_read,\
|
||||
.llseek = noop_llseek,\
|
||||
.mmap = drm_gem_mmap,\
|
||||
}
|
||||
|
||||
void drm_gem_object_release(struct drm_gem_object *obj);
|
||||
void drm_gem_object_free(struct kref *kref);
|
||||
int drm_gem_object_init(struct drm_device *dev,
|
||||
|
|
|
@ -26,6 +26,32 @@ to_drm_gem_cma_obj(struct drm_gem_object *gem_obj)
|
|||
return container_of(gem_obj, struct drm_gem_cma_object, base);
|
||||
}
|
||||
|
||||
/**
|
||||
* DEFINE_DRM_GEM_CMA_FOPS() - macro to generate file operations for CMA drivers
|
||||
* @name: name for the generated structure
|
||||
*
|
||||
* This macro autogenerates a suitable &struct file_operations for CMA based
|
||||
* drivers, which can be assigned to &drm_driver.fops. Note that this structure
|
||||
* cannot be shared between drivers, because it contains a reference to the
|
||||
* current module using THIS_MODULE.
|
||||
*
|
||||
* Note that the declaration is already marked as static - if you need a
|
||||
* non-static version of this you're probably doing it wrong and will break the
|
||||
* THIS_MODULE reference by accident.
|
||||
*/
|
||||
#define DEFINE_DRM_GEM_CMA_FOPS(name) \
|
||||
static const struct file_operations name = {\
|
||||
.owner = THIS_MODULE,\
|
||||
.open = drm_open,\
|
||||
.release = drm_release,\
|
||||
.unlocked_ioctl = drm_ioctl,\
|
||||
.compat_ioctl = drm_compat_ioctl,\
|
||||
.poll = drm_poll,\
|
||||
.read = drm_read,\
|
||||
.llseek = noop_llseek,\
|
||||
.mmap = drm_gem_cma_mmap,\
|
||||
}
|
||||
|
||||
/* free GEM object */
|
||||
void drm_gem_cma_free_object(struct drm_gem_object *gem_obj);
|
||||
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (c) 2015 NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sub license,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DRM_SCDC_HELPER_H
|
||||
#define DRM_SCDC_HELPER_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define SCDC_SINK_VERSION 0x01
|
||||
|
||||
#define SCDC_SOURCE_VERSION 0x02
|
||||
|
||||
#define SCDC_UPDATE_0 0x10
|
||||
#define SCDC_READ_REQUEST_TEST (1 << 2)
|
||||
#define SCDC_CED_UPDATE (1 << 1)
|
||||
#define SCDC_STATUS_UPDATE (1 << 0)
|
||||
|
||||
#define SCDC_UPDATE_1 0x11
|
||||
|
||||
#define SCDC_TMDS_CONFIG 0x20
|
||||
#define SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 (1 << 1)
|
||||
#define SCDC_TMDS_BIT_CLOCK_RATIO_BY_10 (0 << 1)
|
||||
#define SCDC_SCRAMBLING_ENABLE (1 << 0)
|
||||
|
||||
#define SCDC_SCRAMBLER_STATUS 0x21
|
||||
#define SCDC_SCRAMBLING_STATUS (1 << 0)
|
||||
|
||||
#define SCDC_CONFIG_0 0x30
|
||||
#define SCDC_READ_REQUEST_ENABLE (1 << 0)
|
||||
|
||||
#define SCDC_STATUS_FLAGS_0 0x40
|
||||
#define SCDC_CH2_LOCK (1 < 3)
|
||||
#define SCDC_CH1_LOCK (1 < 2)
|
||||
#define SCDC_CH0_LOCK (1 < 1)
|
||||
#define SCDC_CH_LOCK_MASK (SCDC_CH2_LOCK | SCDC_CH1_LOCK | SCDC_CH0_LOCK)
|
||||
#define SCDC_CLOCK_DETECT (1 << 0)
|
||||
|
||||
#define SCDC_STATUS_FLAGS_1 0x41
|
||||
|
||||
#define SCDC_ERR_DET_0_L 0x50
|
||||
#define SCDC_ERR_DET_0_H 0x51
|
||||
#define SCDC_ERR_DET_1_L 0x52
|
||||
#define SCDC_ERR_DET_1_H 0x53
|
||||
#define SCDC_ERR_DET_2_L 0x54
|
||||
#define SCDC_ERR_DET_2_H 0x55
|
||||
#define SCDC_CHANNEL_VALID (1 << 7)
|
||||
|
||||
#define SCDC_ERR_DET_CHECKSUM 0x56
|
||||
|
||||
#define SCDC_TEST_CONFIG_0 0xc0
|
||||
#define SCDC_TEST_READ_REQUEST (1 << 7)
|
||||
#define SCDC_TEST_READ_REQUEST_DELAY(x) ((x) & 0x7f)
|
||||
|
||||
#define SCDC_MANUFACTURER_IEEE_OUI 0xd0
|
||||
#define SCDC_MANUFACTURER_IEEE_OUI_SIZE 3
|
||||
|
||||
#define SCDC_DEVICE_ID 0xd3
|
||||
#define SCDC_DEVICE_ID_SIZE 8
|
||||
|
||||
#define SCDC_DEVICE_HARDWARE_REVISION 0xdb
|
||||
#define SCDC_GET_DEVICE_HARDWARE_REVISION_MAJOR(x) (((x) >> 4) & 0xf)
|
||||
#define SCDC_GET_DEVICE_HARDWARE_REVISION_MINOR(x) (((x) >> 0) & 0xf)
|
||||
|
||||
#define SCDC_DEVICE_SOFTWARE_MAJOR_REVISION 0xdc
|
||||
#define SCDC_DEVICE_SOFTWARE_MINOR_REVISION 0xdd
|
||||
|
||||
#define SCDC_MANUFACTURER_SPECIFIC 0xde
|
||||
#define SCDC_MANUFACTURER_SPECIFIC_SIZE 34
|
||||
|
||||
ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
|
||||
size_t size);
|
||||
ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,
|
||||
const void *buffer, size_t size);
|
||||
|
||||
/**
|
||||
* drm_scdc_readb - read a single byte from SCDC
|
||||
* @adapter: I2C adapter
|
||||
* @offset: offset of register to read
|
||||
* @value: return location for the register value
|
||||
*
|
||||
* Reads a single byte from SCDC. This is a convenience wrapper around the
|
||||
* drm_scdc_read() function.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*/
|
||||
static inline int drm_scdc_readb(struct i2c_adapter *adapter, u8 offset,
|
||||
u8 *value)
|
||||
{
|
||||
return drm_scdc_read(adapter, offset, value, sizeof(*value));
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_scdc_writeb - write a single byte to SCDC
|
||||
* @adapter: I2C adapter
|
||||
* @offset: offset of register to read
|
||||
* @value: return location for the register value
|
||||
*
|
||||
* Writes a single byte to SCDC. This is a convenience wrapper around the
|
||||
* drm_scdc_write() function.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*/
|
||||
static inline int drm_scdc_writeb(struct i2c_adapter *adapter, u8 offset,
|
||||
u8 value)
|
||||
{
|
||||
return drm_scdc_write(adapter, offset, &value, sizeof(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_scdc_set_scrambling - enable scrambling
|
||||
* @adapter: I2C adapter for DDC channel
|
||||
* @enable: bool to indicate if scrambling is to be enabled/disabled
|
||||
*
|
||||
* Writes the TMDS config register over SCDC channel, and:
|
||||
* enables scrambling when enable = 1
|
||||
* disables scrambling when enable = 0
|
||||
*
|
||||
* Returns:
|
||||
* True if scrambling is set/reset successfully, false otherwise.
|
||||
*/
|
||||
bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable);
|
||||
|
||||
/**
|
||||
* drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio
|
||||
* @adapter: I2C adapter for DDC channel
|
||||
* @set: ret or reset the high clock ratio
|
||||
*
|
||||
* Writes to the TMDS config register over SCDC channel, and:
|
||||
* sets TMDS clock ratio to 1/40 when set = 1
|
||||
* sets TMDS clock ratio to 1/10 when set = 0
|
||||
*
|
||||
* Returns:
|
||||
* True if write is successful, false otherwise.
|
||||
*/
|
||||
bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set);
|
||||
#endif
|
|
@ -83,4 +83,6 @@ struct dma_fence_array *dma_fence_array_create(int num_fences,
|
|||
u64 context, unsigned seqno,
|
||||
bool signal_on_any);
|
||||
|
||||
bool dma_fence_match_context(struct dma_fence *fence, u64 context);
|
||||
|
||||
#endif /* __LINUX_DMA_FENCE_ARRAY_H */
|
||||
|
|
|
@ -35,6 +35,7 @@ enum hdmi_infoframe_type {
|
|||
};
|
||||
|
||||
#define HDMI_IEEE_OUI 0x000c03
|
||||
#define HDMI_FORUM_IEEE_OUI 0xc45dd8
|
||||
#define HDMI_INFOFRAME_HEADER_SIZE 4
|
||||
#define HDMI_AVI_INFOFRAME_SIZE 13
|
||||
#define HDMI_SPD_INFOFRAME_SIZE 25
|
||||
|
|
Загрузка…
Ссылка в новой задаче