Commit edd849e5 authored by Dave Airlie's avatar Dave Airlie

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
  ...
parents be5df20a 62c58af3
...@@ -299,5 +299,5 @@ What: /sys/bus/pci/devices/.../revision ...@@ -299,5 +299,5 @@ What: /sys/bus/pci/devices/.../revision
Date: November 2016 Date: November 2016
Contact: Emil Velikov <emil.l.velikov@gmail.com> Contact: Emil Velikov <emil.l.velikov@gmail.com>
Description: 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. The value comes from device config space. The file is read only.
...@@ -14,14 +14,17 @@ Contact ...@@ -14,14 +14,17 @@ Contact
The Linux kernel security team can be contacted by email at The Linux kernel security team can be contacted by email at
<security@kernel.org>. This is a private list of security officers <security@kernel.org>. This is a private list of security officers
who will help verify the bug report and develop and release a fix. 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 If you already have a fix, please include it with your report, as
area maintainers to understand and fix the security vulnerability. 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 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 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. admin-guide/reporting-bugs.rst if you are unclear about what
Any exploit code is very helpful and will not be released without information is helpful. Any exploit code is very helpful and will not
consent from the reporter unless it has already been made public. be released without consent from the reporter unless it has already been
made public.
Disclosure Disclosure
---------- ----------
...@@ -39,6 +42,32 @@ disclosure is from immediate (esp. if it's already publicly known) ...@@ -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 to a few weeks. As a basic default policy, we expect report date to
disclosure date to be on the order of 7 days. 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 Non-disclosure agreements
------------------------- -------------------------
......
...@@ -34,7 +34,7 @@ from load_config import loadConfig ...@@ -34,7 +34,7 @@ from load_config import loadConfig
# Add any Sphinx extension module names here, as strings. They can be # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones. # 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 # The name of the math extension changed on Sphinx 1.4
if major == 1 and minor > 3: if major == 1 and minor > 3:
......
...@@ -231,7 +231,7 @@ the reference implementation in drivers/cpufreq/longrun.c ...@@ -231,7 +231,7 @@ the reference implementation in drivers/cpufreq/longrun.c
Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset. Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset.
get_intermediate should return a stable intermediate frequency platform wants to 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 jumping to the frequency corresponding to 'index'. Core will take care of
sending notifications and driver doesn't have to handle them in sending notifications and driver doesn't have to handle them in
target_intermediate() or target_index(). target_intermediate() or target_index().
......
...@@ -34,6 +34,9 @@ Optional properties for HDMI: ...@@ -34,6 +34,9 @@ Optional properties for HDMI:
- hpd-gpios: The GPIO pin for HDMI hotplug detect (if it doesn't appear - hpd-gpios: The GPIO pin for HDMI hotplug detect (if it doesn't appear
as an interrupt/status bit in the HDMI controller as an interrupt/status bit in the HDMI controller
itself). See bindings/pinctrl/brcm,bcm2835-gpio.txt 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: Required properties for DPI:
- compatible: Should be "brcm,bcm2835-dpi" - compatible: Should be "brcm,bcm2835-dpi"
......
graph G {
Hello -- World
}
...@@ -34,8 +34,9 @@ format-specific subdirectories under ``Documentation/output``. ...@@ -34,8 +34,9 @@ format-specific subdirectories under ``Documentation/output``.
To generate documentation, Sphinx (``sphinx-build``) must obviously be To generate documentation, Sphinx (``sphinx-build``) must obviously be
installed. For prettier HTML output, the Read the Docs Sphinx theme installed. For prettier HTML output, the Read the Docs Sphinx theme
(``sphinx_rtd_theme``) is used if available. For PDF output, ``rst2pdf`` is also (``sphinx_rtd_theme``) is used if available. For PDF output you'll also need
needed. All of these are widely available and packaged in distributions. ``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 To pass extra options to Sphinx, you can use the ``SPHINXOPTS`` make
variable. For example, use ``make SPHINXOPTS=-v htmldocs`` to get more verbose variable. For example, use ``make SPHINXOPTS=-v htmldocs`` to get more verbose
...@@ -73,7 +74,16 @@ Specific guidelines for the kernel documentation ...@@ -73,7 +74,16 @@ Specific guidelines for the kernel documentation
Here are some 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: * Please stick to this order of heading adornments:
...@@ -103,6 +113,12 @@ Here are some specific guidelines for the kernel documentation: ...@@ -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 the order as encountered."), having the higher levels the same overall makes
it easier to follow the documents. 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 the C domain
------------ ------------
...@@ -217,3 +233,96 @@ Rendered as: ...@@ -217,3 +233,96 @@ Rendered as:
* .. _`last row`: * .. _`last row`:
- column 3 - 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>
<?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>
...@@ -140,12 +140,12 @@ Device Instance and Driver Handling ...@@ -140,12 +140,12 @@ Device Instance and Driver Handling
.. kernel-doc:: drivers/gpu/drm/drm_drv.c .. kernel-doc:: drivers/gpu/drm/drm_drv.c
:doc: driver instance overview :doc: driver instance overview
.. kernel-doc:: drivers/gpu/drm/drm_drv.c
:export:
.. kernel-doc:: include/drm/drm_drv.h .. kernel-doc:: include/drm/drm_drv.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_drv.c
:export:
Driver Load Driver Load
----------- -----------
...@@ -243,61 +243,15 @@ drivers. ...@@ -243,61 +243,15 @@ drivers.
Open/Close, File Operations and IOCTLs 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 File Operations
--------------- ---------------
.. kernel-doc:: drivers/gpu/drm/drm_file.c .. kernel-doc:: drivers/gpu/drm/drm_file.c
:doc: file operations :doc: file operations
.. kernel-doc:: include/drm/drm_file.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_file.c .. kernel-doc:: drivers/gpu/drm/drm_file.c
:export: :export:
......
...@@ -37,10 +37,12 @@ Modeset Helper Reference for Common Vtables ...@@ -37,10 +37,12 @@ Modeset Helper Reference for Common Vtables
=========================================== ===========================================
.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h .. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
:internal: :doc: overview
.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h .. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
:doc: overview :internal:
.. _drm_atomic_helper:
Atomic Modeset Helper Functions Reference Atomic Modeset Helper Functions Reference
========================================= =========================================
...@@ -84,27 +86,27 @@ Legacy CRTC/Modeset Helper Functions Reference ...@@ -84,27 +86,27 @@ Legacy CRTC/Modeset Helper Functions Reference
Simple KMS Helper 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 .. kernel-doc:: include/drm/drm_simple_kms_helper.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c .. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
:export: :export:
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
:doc: overview
fbdev Helper Functions Reference fbdev Helper Functions Reference
================================ ================================
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c .. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
:doc: fbdev helpers :doc: fbdev helpers
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
:export:
.. kernel-doc:: include/drm/drm_fb_helper.h .. kernel-doc:: include/drm/drm_fb_helper.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
:export:
Framebuffer CMA Helper Functions Reference Framebuffer CMA Helper Functions Reference
========================================== ==========================================
...@@ -114,6 +116,8 @@ Framebuffer CMA Helper Functions Reference ...@@ -114,6 +116,8 @@ Framebuffer CMA Helper Functions Reference
.. kernel-doc:: drivers/gpu/drm/drm_fb_cma_helper.c .. kernel-doc:: drivers/gpu/drm/drm_fb_cma_helper.c
:export: :export:
.. _drm_bridges:
Bridges Bridges
======= =======
...@@ -139,18 +143,20 @@ Bridge Helper Reference ...@@ -139,18 +143,20 @@ Bridge Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c .. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:export: :export:
.. _drm_panel_helper:
Panel Helper Reference Panel Helper Reference
====================== ======================
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
:doc: drm panel
.. kernel-doc:: include/drm/drm_panel.h .. kernel-doc:: include/drm/drm_panel.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_panel.c .. kernel-doc:: drivers/gpu/drm/drm_panel.c
:export: :export:
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
:doc: drm panel
Display Port Helper Functions Reference Display Port Helper Functions Reference
======================================= =======================================
...@@ -217,6 +223,18 @@ EDID Helper Functions Reference ...@@ -217,6 +223,18 @@ EDID Helper Functions Reference
.. kernel-doc:: drivers/gpu/drm/drm_edid.c .. kernel-doc:: drivers/gpu/drm/drm_edid.c
:export: :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 Rectangle Utilities Reference
============================= =============================
......
...@@ -15,35 +15,271 @@ be setup by initializing the following fields. ...@@ -15,35 +15,271 @@ be setup by initializing the following fields.
- struct drm_mode_config_funcs \*funcs; - struct drm_mode_config_funcs \*funcs;
Mode setting functions. 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 KMS Core Structures and Functions
================================= =================================
.. kernel-doc:: drivers/gpu/drm/drm_mode_config.c
:export:
.. kernel-doc:: include/drm/drm_mode_config.h .. kernel-doc:: include/drm/drm_mode_config.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_mode_config.c
:export:
Modeset Base Object Abstraction 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 .. kernel-doc:: include/drm/drm_mode_object.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_mode_object.c .. kernel-doc:: drivers/gpu/drm/drm_mode_object.c
:export: :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 .. kernel-doc:: include/drm/drm_atomic.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_atomic.c
:export:
CRTC Abstraction CRTC Abstraction
================ ================
...@@ -68,12 +304,12 @@ Frame Buffer Abstraction ...@@ -68,12 +304,12 @@ Frame Buffer Abstraction
Frame Buffer Functions Reference Frame Buffer Functions Reference
-------------------------------- --------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_framebuffer.c
:export:
.. kernel-doc:: include/drm/drm_framebuffer.h .. kernel-doc:: include/drm/drm_framebuffer.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_framebuffer.c
:export:
DRM Format Handling DRM Format Handling
=================== ===================
...@@ -376,8 +612,8 @@ operation handler. ...@@ -376,8 +612,8 @@ operation handler.
Vertical Blanking and Interrupt Handling Functions Reference Vertical Blanking and Interrupt Handling Functions Reference
------------------------------------------------------------ ------------------------------------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_irq.c
:export:
.. kernel-doc:: include/drm/drm_irq.h .. kernel-doc:: include/drm/drm_irq.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_irq.c
:export:
...@@ -365,36 +365,36 @@ from the client in libdrm. ...@@ -365,36 +365,36 @@ from the client in libdrm.
GEM Function Reference GEM Function Reference
---------------------- ----------------------
.. kernel-doc:: drivers/gpu/drm/drm_gem.c
:export:
.. kernel-doc:: include/drm/drm_gem.h .. kernel-doc:: include/drm/drm_gem.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_gem.c
:export:
GEM CMA Helper Functions Reference GEM CMA Helper Functions Reference
---------------------------------- ----------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c .. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
:doc: cma helpers :doc: cma helpers
.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
:export:
.. kernel-doc:: include/drm/drm_gem_cma_helper.h .. kernel-doc:: include/drm/drm_gem_cma_helper.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
:export:
VMA Offset Manager VMA Offset Manager
================== ==================
.. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c .. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c
:doc: vma offset manager :doc: vma offset manager
.. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c
:export:
.. kernel-doc:: include/drm/drm_vma_manager.h .. kernel-doc:: include/drm/drm_vma_manager.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c
:export:
PRIME Buffer Sharing PRIME Buffer Sharing
==================== ====================
...@@ -473,12 +473,12 @@ LRU Scan/Eviction Support ...@@ -473,12 +473,12 @@ LRU Scan/Eviction Support
DRM MM Range Allocator Function References DRM MM Range Allocator Function References
------------------------------------------ ------------------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_mm.c
:export:
.. kernel-doc:: include/drm/drm_mm.h .. kernel-doc:: include/drm/drm_mm.h
:internal: :internal:
.. kernel-doc:: drivers/gpu/drm/drm_mm.c
:export:
DRM Cache Handling DRM Cache Handling
================== ==================
......
...@@ -21,6 +21,8 @@ libdrm Device Lookup ...@@ -21,6 +21,8 @@ libdrm Device Lookup
:doc: getunique and setversion story :doc: getunique and setversion story
.. _drm_primary_node:
Primary Nodes, DRM Master and Authentication Primary Nodes, DRM Master and Authentication
============================================ ============================================
...@@ -103,6 +105,8 @@ is already rather painful for the DRM subsystem, with multiple different uAPIs ...@@ -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 for the same thing co-existing. If we add a few more complete mistakes into the
mix every year it would be entirely unmanageable. mix every year it would be entirely unmanageable.
.. _drm_render_node:
Render nodes Render nodes
============ ============
......
...@@ -97,7 +97,7 @@ should contain the phy name as given in the dt data and in the case of ...@@ -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 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 devm_phy_get associates the device with the PHY using devres on
successful PHY get. On driver detach, release function is invoked 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 devm_phy_optional_get should be used when the phy is optional. These
two functions will never return -ENODEV, but instead returns NULL when 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 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. ...@@ -318,9 +318,10 @@ PDF outputs, it is recommended to use version 1.4.6.
.. note:: .. note::
Please notice that, for PDF and LaTeX output, you'll also need ``XeLaTeX`` Please notice that, for PDF and LaTeX output, you'll also need ``XeLaTeX``
version 3.14159265. Depending on the distribution, you may also need version 3.14159265. Depending on the distribution, you may also need to
to install a series of ``texlive`` packages that provide the minimal install a series of ``texlive`` packages that provide the minimal set of
set of functionalities required for ``XeLaTex`` to work. functionalities required for ``XeLaTex`` to work. For PDF output you'll also
need ``convert(1)`` from ImageMagick (https://www.imagemagick.org).
Other tools Other tools
----------- -----------
......
# -*- 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, ...@@ -144,3 +144,29 @@ struct dma_fence_array *dma_fence_array_create(int num_fences,
return array; return array;
} }
EXPORT_SYMBOL(dma_fence_array_create); 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 ...@@ -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_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_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
drm_kms_helper_common.o drm_dp_dual_mode_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_LOAD_EDID_FIRMWARE) += drm_edid_load.o
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.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) ...@@ -239,17 +239,7 @@ static int hdlcd_debugfs_init(struct drm_minor *minor)
} }
#endif #endif
static const struct file_operations fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver hdlcd_driver = { static struct drm_driver hdlcd_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
......
...@@ -178,17 +178,7 @@ static void malidp_lastclose(struct drm_device *drm) ...@@ -178,17 +178,7 @@ static void malidp_lastclose(struct drm_device *drm)
drm_fbdev_cma_restore_mode(malidp->fbdev); drm_fbdev_cma_restore_mode(malidp->fbdev);
} }
static const struct file_operations fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver malidp_driver = { static struct drm_driver malidp_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
......
...@@ -60,16 +60,7 @@ static void armada_drm_lastclose(struct drm_device *dev) ...@@ -60,16 +60,7 @@ static void armada_drm_lastclose(struct drm_device *dev)
armada_fbdev_lastclose(dev); armada_fbdev_lastclose(dev);
} }
static const struct file_operations armada_drm_fops = { DEFINE_DRM_GEM_FOPS(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,
};
static struct drm_driver armada_drm_driver = { static struct drm_driver armada_drm_driver = {
.lastclose = armada_drm_lastclose, .lastclose = armada_drm_lastclose,
......
...@@ -55,14 +55,12 @@ drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state) ...@@ -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 * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
* @event: pointer to the current page flip event * @event: pointer to the current page flip event
* @id: CRTC id (returned by drm_crtc_index) * @id: CRTC id (returned by drm_crtc_index)
* @enabled: CRTC state
*/ */
struct atmel_hlcdc_crtc { struct atmel_hlcdc_crtc {
struct drm_crtc base; struct drm_crtc base;
struct atmel_hlcdc_dc *dc; struct atmel_hlcdc_dc *dc;
struct drm_pending_vblank_event *event; struct drm_pending_vblank_event *event;
int id; int id;
bool enabled;
}; };
static inline struct atmel_hlcdc_crtc * static inline struct atmel_hlcdc_crtc *
...@@ -158,9 +156,6 @@ static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) ...@@ -158,9 +156,6 @@ static void atmel_hlcdc_crtc_disable(struct drm_crtc *c)
struct regmap *regmap = crtc->dc->hlcdc->regmap; struct regmap *regmap = crtc->dc->hlcdc->regmap;
unsigned int status; unsigned int status;
if (!crtc->enabled)
return;
drm_crtc_vblank_off(c); drm_crtc_vblank_off(c);
pm_runtime_get_sync(dev->dev); pm_runtime_get_sync(dev->dev);
...@@ -186,8 +181,6 @@ static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) ...@@ -186,8 +181,6 @@ static void atmel_hlcdc_crtc_disable(struct drm_crtc *c)
pm_runtime_allow(dev->dev); pm_runtime_allow(dev->dev);
pm_runtime_put_sync(dev->dev); pm_runtime_put_sync(dev->dev);
crtc->enabled = false;
} }
static void atmel_hlcdc_crtc_enable(struct drm_crtc *c) static void atmel_hlcdc_crtc_enable(struct drm_crtc *c)
...@@ -197,9 +190,6 @@ 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; struct regmap *regmap = crtc->dc->hlcdc->regmap;
unsigned int status; unsigned int status;
if (crtc->enabled)
return;
pm_runtime_get_sync(dev->dev); pm_runtime_get_sync(dev->dev);
pm_runtime_forbid(dev->dev); pm_runtime_forbid(dev->dev);
...@@ -226,29 +216,6 @@ static void atmel_hlcdc_crtc_enable(struct drm_crtc *c) ...@@ -226,29 +216,6 @@ static void atmel_hlcdc_crtc_enable(struct drm_crtc *c)
pm_runtime_put_sync(dev->dev); pm_runtime_put_sync(dev->dev);
drm_crtc_vblank_on(c); 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) #define ATMEL_HLCDC_RGB444_OUTPUT BIT(0)
......
...@@ -724,17 +724,7 @@ static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) ...@@ -724,17 +724,7 @@ static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev)
regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
} }
static const struct file_operations fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver atmel_hlcdc_dc_driver = { static struct drm_driver atmel_hlcdc_dc_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
...@@ -810,31 +800,32 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) ...@@ -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) static int atmel_hlcdc_dc_drm_suspend(struct device *dev)
{ {
struct drm_device *drm_dev = dev_get_drvdata(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;
state = drm_atomic_helper_suspend(drm_dev);
if (IS_ERR(state))
return PTR_ERR(state);
if (pm_runtime_suspended(dev)) dc->suspend.state = state;
return 0;
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; return 0;
} }
static int atmel_hlcdc_dc_drm_resume(struct device *dev) static int atmel_hlcdc_dc_drm_resume(struct device *dev)
{ {
struct drm_device *drm_dev = dev_get_drvdata(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)) clk_prepare_enable(dc->hlcdc->periph_clk);
return 0; regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr);
drm_modeset_lock_all(drm_dev); return drm_atomic_helper_resume(drm_dev, dc->suspend.state);
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;
} }
#endif #endif
......
...@@ -358,6 +358,7 @@ struct atmel_hlcdc_plane_properties { ...@@ -358,6 +358,7 @@ struct atmel_hlcdc_plane_properties {
* @planes: instantiated planes * @planes: instantiated planes
* @layers: active HLCDC layers * @layers: active HLCDC layers
* @wq: display controller workqueue * @wq: display controller workqueue
* @suspend: used to store the HLCDC state when entering suspend
* @commit: used for async commit handling * @commit: used for async commit handling
*/ */
struct atmel_hlcdc_dc { struct atmel_hlcdc_dc {
...@@ -368,6 +369,10 @@ struct atmel_hlcdc_dc { ...@@ -368,6 +369,10 @@ struct atmel_hlcdc_dc {
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS]; struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
struct workqueue_struct *wq; struct workqueue_struct *wq;
struct {
u32 imr;
struct drm_atomic_state *state;
} suspend;
struct { struct {
wait_queue_head_t wait; wait_queue_head_t wait;
bool pending; bool pending;
...@@ -428,9 +433,6 @@ int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state); ...@@ -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_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_crtc_create(struct drm_device *dev);
int atmel_hlcdc_create_outputs(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, ...@@ -132,6 +132,7 @@ static int bochsfb_create(struct drm_fb_helper *helper,
info->fix.smem_start = 0; info->fix.smem_start = 0;
info->fix.smem_len = size; info->fix.smem_len = size;
bochs->fb.initialized = true;
return 0; return 0;
} }
...@@ -148,7 +149,6 @@ static int bochs_fbdev_destroy(struct bochs_device *bochs) ...@@ -148,7 +149,6 @@ static int bochs_fbdev_destroy(struct bochs_device *bochs)
gfb->obj = NULL; gfb->obj = NULL;
} }
drm_fb_helper_fini(&bochs->fb.helper);
drm_framebuffer_unregister_private(&gfb->base); drm_framebuffer_unregister_private(&gfb->base);
drm_framebuffer_cleanup(&gfb->base); drm_framebuffer_cleanup(&gfb->base);
...@@ -180,7 +180,6 @@ int bochs_fbdev_init(struct bochs_device *bochs) ...@@ -180,7 +180,6 @@ int bochs_fbdev_init(struct bochs_device *bochs)
if (ret) if (ret)
goto fini; goto fini;
bochs->fb.initialized = true;
return 0; return 0;
fini: fini:
...@@ -190,9 +189,9 @@ int bochs_fbdev_init(struct bochs_device *bochs) ...@@ -190,9 +189,9 @@ int bochs_fbdev_init(struct bochs_device *bochs)
void bochs_fbdev_fini(struct bochs_device *bochs) void bochs_fbdev_fini(struct bochs_device *bochs)
{ {
if (!bochs->fb.initialized) if (bochs->fb.initialized)
return; bochs_fbdev_destroy(bochs);
bochs_fbdev_destroy(bochs); drm_fb_helper_fini(&bochs->fb.helper);
bochs->fb.initialized = false; bochs->fb.initialized = false;
} }
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "dw-hdmi.h" #include "dw-hdmi.h"
#include "dw-hdmi-audio.h" #include "dw-hdmi-audio.h"
#define DDC_SEGMENT_ADDR 0x30
#define HDMI_EDID_LEN 512 #define HDMI_EDID_LEN 512
#define RGB 0 #define RGB 0
...@@ -112,6 +113,7 @@ struct dw_hdmi_i2c { ...@@ -112,6 +113,7 @@ struct dw_hdmi_i2c {
u8 slave_reg; u8 slave_reg;
bool is_regaddr; bool is_regaddr;
bool is_segment;
}; };
struct dw_hdmi_phy_data { struct dw_hdmi_phy_data {
...@@ -247,8 +249,12 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, ...@@ -247,8 +249,12 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
reinit_completion(&i2c->cmp); reinit_completion(&i2c->cmp);
hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS); hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, if (i2c->is_segment)
HDMI_I2CM_OPERATION); 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); stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
if (!stat) if (!stat)
...@@ -260,6 +266,7 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, ...@@ -260,6 +266,7 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
*buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI); *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI);
} }
i2c->is_segment = false;
return 0; return 0;
} }
...@@ -309,12 +316,6 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, ...@@ -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); dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr);
for (i = 0; i < num; i++) { 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) { if (msgs[i].len == 0) {
dev_dbg(hdmi->dev, dev_dbg(hdmi->dev,
"unsupported transfer %d/%d, no data\n", "unsupported transfer %d/%d, no data\n",
...@@ -334,15 +335,24 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, ...@@ -334,15 +335,24 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
/* Set slave device register address on transfer */ /* Set slave device register address on transfer */
i2c->is_regaddr = false; i2c->is_regaddr = false;
/* Set segment pointer for I2C extended read mode operation */
i2c->is_segment = false;
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n", dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
i + 1, num, msgs[i].len, msgs[i].flags); i + 1, num, msgs[i].len, msgs[i].flags);
if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
if (msgs[i].flags & I2C_M_RD) i2c->is_segment = true;
ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, msgs[i].len); hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR);
else hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR);
ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, msgs[i].len); } 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) if (ret < 0)
break; break;
} }
...@@ -1230,6 +1240,58 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) ...@@ -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); 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, static void hdmi_av_composer(struct dw_hdmi *hdmi,
const struct drm_display_mode *mode) const struct drm_display_mode *mode)
{ {
...@@ -1479,6 +1541,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, 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 Initialization Step F - Configure AVI InfoFrame */
hdmi_config_AVI(hdmi, mode); hdmi_config_AVI(hdmi, mode);
hdmi_config_vendor_specific_infoframe(hdmi, mode);
} else { } else {
dev_dbg(hdmi->dev, "%s DVI mode\n", __func__); dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
} }
......
...@@ -854,6 +854,10 @@ enum { ...@@ -854,6 +854,10 @@ enum {
HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10, HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, 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 */ /* PHY_CONF0 field values */
HDMI_PHY_CONF0_PDZ_MASK = 0x80, HDMI_PHY_CONF0_PDZ_MASK = 0x80,
HDMI_PHY_CONF0_PDZ_OFFSET = 7, HDMI_PHY_CONF0_PDZ_OFFSET = 7,
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_encoder.h> #include <drm/drm_encoder.h>
#include <drm/drm_displayid.h> #include <drm/drm_displayid.h>
#include <drm/drm_scdc_helper.h>
#include "drm_crtc_internal.h" #include "drm_crtc_internal.h"
...@@ -3248,6 +3249,21 @@ static bool cea_db_is_hdmi_vsdb(const u8 *db) ...@@ -3248,6 +3249,21 @@ static bool cea_db_is_hdmi_vsdb(const u8 *db)
return hdmi_id == HDMI_IEEE_OUI; 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) \ #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) 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) ...@@ -3799,6 +3815,48 @@ drm_default_rgb_quant_range(const struct drm_display_mode *mode)
} }
EXPORT_SYMBOL(drm_default_rgb_quant_range); 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, static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
const u8 *hdmi) const u8 *hdmi)
{ {
...@@ -3913,6 +3971,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector, ...@@ -3913,6 +3971,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
if (cea_db_is_hdmi_vsdb(db)) if (cea_db_is_hdmi_vsdb(db))
drm_parse_hdmi_vsdb_video(connector, 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); ...@@ -50,12 +50,14 @@ DEFINE_MUTEX(drm_global_mutex);
* *
* Drivers must define the file operations structure that forms the DRM * Drivers must define the file operations structure that forms the DRM
* userspace API entry point, even though most of those operations are * 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 * 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 * Note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n, so there's no
* implement private ioctls that require 32/64 bit compatibility support must * need to sprinkle #ifdef into the code. Drivers which implement private ioctls
* provide their own .compat_ioctl() handler that processes private ioctls and * that require 32/64 bit compatibility support must provide their own
* calls drm_compat_ioctl() for core ioctls. * &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 * 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 * events are a generic and extensible means to send asynchronous events to
...@@ -63,10 +65,14 @@ DEFINE_MUTEX(drm_global_mutex); ...@@ -63,10 +65,14 @@ DEFINE_MUTEX(drm_global_mutex);
* page flip completions by the KMS API. But drivers can also use it for their * page flip completions by the KMS API. But drivers can also use it for their
* own needs, e.g. to signal completion of rendering. * 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 * The memory mapping implementation will vary depending on how the driver
* manages memory. Legacy drivers will use the deprecated drm_legacy_mmap() * manages memory. Legacy drivers will use the deprecated drm_legacy_mmap()
* function, modern drivers should use one of the provided memory-manager * 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 * No other file operations are supported by the DRM userspace API. Overall the
* following is an example #file_operations structure:: * following is an example #file_operations structure::
...@@ -82,6 +88,10 @@ DEFINE_MUTEX(drm_global_mutex); ...@@ -82,6 +88,10 @@ DEFINE_MUTEX(drm_global_mutex);
* .llseek = no_llseek, * .llseek = no_llseek,
* .mmap = drm_gem_mmap, * .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); static int drm_open_helper(struct file *filp, struct drm_minor *minor);
...@@ -111,9 +121,9 @@ static int drm_setup(struct drm_device * dev) ...@@ -111,9 +121,9 @@ static int drm_setup(struct drm_device * dev)
* @inode: device inode * @inode: device inode
* @filp: file pointer. * @filp: file pointer.
* *
* This function must be used by drivers as their .open() #file_operations * This function must be used by drivers as their &file_operations.open method.
* method. It looks up the correct DRM device and instantiates all the per-file * It looks up the correct DRM device and instantiates all the per-file
* resources for it. * resources for it. It also calls the &drm_driver.open driver callback.
* *
* RETURNS: * RETURNS:
* *
...@@ -298,11 +308,6 @@ static void drm_events_release(struct drm_file *file_priv) ...@@ -298,11 +308,6 @@ static void drm_events_release(struct drm_file *file_priv)
spin_unlock_irqrestore(&dev->event_lock, flags); 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) static void drm_legacy_dev_reinit(struct drm_device *dev)
{ {
if (dev->irq_enabled) if (dev->irq_enabled)
...@@ -327,15 +332,6 @@ static void drm_legacy_dev_reinit(struct drm_device *dev) ...@@ -327,15 +332,6 @@ static void drm_legacy_dev_reinit(struct drm_device *dev)
DRM_DEBUG("lastclose completed\n"); 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) void drm_lastclose(struct drm_device * dev)
{ {
DRM_DEBUG("\n"); DRM_DEBUG("\n");
...@@ -353,9 +349,11 @@ void drm_lastclose(struct drm_device * dev) ...@@ -353,9 +349,11 @@ void drm_lastclose(struct drm_device * dev)
* @inode: device inode * @inode: device inode
* @filp: file pointer. * @filp: file pointer.
* *
* This function must be used by drivers as their .release() #file_operations * This function must be used by drivers as their &file_operations.release
* method. It frees any resources associated with the open file, and if this is * method. It frees any resources associated with the open file, and calls the
* the last open file for the DRM device also proceeds to call drm_lastclose(). * &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: * RETURNS:
* *
...@@ -443,13 +441,13 @@ EXPORT_SYMBOL(drm_release); ...@@ -443,13 +441,13 @@ EXPORT_SYMBOL(drm_release);
* @count: count in bytes to read * @count: count in bytes to read
* @offset: offset 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. * 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 * Since events are used by the KMS API for vblank and page flip completion this
* means all modern display drivers must use it. * means all modern display drivers must use it.
* *
* @offset is ignore, DRM events are read like a pipe. Therefore drivers also * @offset is ignored, DRM events are read like a pipe. Therefore drivers also
* must set the .llseek() #file_operation to no_llseek(). Polling support is * must set the &file_operation.llseek to no_llseek(). Polling support is
* provided by drm_poll(). * provided by drm_poll().
* *
* This function will only ever read a full event. Therefore userspace must * This function will only ever read a full event. Therefore userspace must
...@@ -537,10 +535,10 @@ EXPORT_SYMBOL(drm_read); ...@@ -537,10 +535,10 @@ EXPORT_SYMBOL(drm_read);
* @filp: file pointer * @filp: file pointer
* @wait: poll waiter table * @wait: poll waiter table
* *
* 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
* method iff they use DRM events for asynchronous signalling to userspace. * iff they use DRM events for asynchronous signalling to userspace. Since
* Since events are used by the KMS API for vblank and page flip completion this * events are used by the KMS API for vblank and page flip completion this means
* means all modern display drivers must use it. * all modern display drivers must use it.
* *
* See also drm_read(). * See also drm_read().
* *
...@@ -650,7 +648,8 @@ EXPORT_SYMBOL(drm_event_reserve_init); ...@@ -650,7 +648,8 @@ EXPORT_SYMBOL(drm_event_reserve_init);
* @p: tracking structure for the pending event * @p: tracking structure for the pending event
* *
* This function frees the event @p initialized with drm_event_reserve_init() * 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, void drm_event_cancel_free(struct drm_device *dev,
struct drm_pending_event *p) struct drm_pending_event *p)
......
...@@ -338,6 +338,9 @@ static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj, ...@@ -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 * as their ->mmap() handler in the DRM device file's file_operations
* structure. * structure.
* *
* Instead of directly referencing this function, drivers should use the
* DEFINE_DRM_GEM_CMA_FOPS().macro.
*
* Returns: * Returns:
* 0 on success or a negative error code on failure. * 0 on success or a negative error code on failure.
*/ */
......
...@@ -978,7 +978,7 @@ static void send_vblank_event(struct drm_device *dev, ...@@ -978,7 +978,7 @@ static void send_vblank_event(struct drm_device *dev,
e->event.tv_sec = now->tv_sec; e->event.tv_sec = now->tv_sec;
e->event.tv_usec = now->tv_usec; 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); e->event.sequence);
drm_send_event_locked(dev, &e->base); drm_send_event_locked(dev, &e->base);
...@@ -1198,9 +1198,9 @@ static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) ...@@ -1198,9 +1198,9 @@ static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
if (atomic_dec_and_test(&vblank->refcount)) { if (atomic_dec_and_test(&vblank->refcount)) {
if (drm_vblank_offdelay == 0) if (drm_vblank_offdelay == 0)
return; return;
else if (dev->vblank_disable_immediate || drm_vblank_offdelay < 0) else if (drm_vblank_offdelay < 0)
vblank_disable_fn((unsigned long)vblank); vblank_disable_fn((unsigned long)vblank);
else else if (!dev->vblank_disable_immediate)
mod_timer(&vblank->disable_timer, mod_timer(&vblank->disable_timer,
jiffies + ((drm_vblank_offdelay * HZ)/1000)); jiffies + ((drm_vblank_offdelay * HZ)/1000));
} }
...@@ -1505,7 +1505,6 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, ...@@ -1505,7 +1505,6 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
} }
e->pipe = pipe; e->pipe = pipe;
e->base.pid = current->pid;
e->event.base.type = DRM_EVENT_VBLANK; e->event.base.type = DRM_EVENT_VBLANK;
e->event.base.length = sizeof(e->event); e->event.base.length = sizeof(e->event);
e->event.user_data = vblwait->request.signal; e->event.user_data = vblwait->request.signal;
...@@ -1534,7 +1533,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, ...@@ -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", DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
vblwait->request.sequence, seq, pipe); vblwait->request.sequence, seq, pipe);
trace_drm_vblank_event_queued(current->pid, pipe, trace_drm_vblank_event_queued(file_priv, pipe,
vblwait->request.sequence); vblwait->request.sequence);
e->event.sequence = vblwait->request.sequence; e->event.sequence = vblwait->request.sequence;
...@@ -1611,7 +1610,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, ...@@ -1611,7 +1610,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
ret = drm_vblank_get(dev, pipe); ret = drm_vblank_get(dev, pipe);
if (ret) { 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; return ret;
} }
seq = drm_vblank_count(dev, pipe); seq = drm_vblank_count(dev, pipe);
...@@ -1639,13 +1638,15 @@ int drm_wait_vblank(struct drm_device *dev, void *data, ...@@ -1639,13 +1638,15 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
return drm_queue_vblank_event(dev, pipe, vblwait, file_priv); return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
} }
DRM_DEBUG("waiting on vblank count %u, crtc %u\n", if (vblwait->request.sequence != seq) {
vblwait->request.sequence, pipe); DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, vblwait->request.sequence, pipe);
(((drm_vblank_count(dev, pipe) - DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
vblwait->request.sequence) <= (1 << 23)) || (((drm_vblank_count(dev, pipe) -
!vblank->enabled || vblwait->request.sequence) <= (1 << 23)) ||
!dev->irq_enabled)); !vblank->enabled ||
!dev->irq_enabled));
}
if (ret != -EINTR) { if (ret != -EINTR) {
struct timeval now; struct timeval now;
...@@ -1654,10 +1655,10 @@ int drm_wait_vblank(struct drm_device *dev, void *data, ...@@ -1654,10 +1655,10 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
vblwait->reply.tval_sec = now.tv_sec; vblwait->reply.tval_sec = now.tv_sec;
vblwait->reply.tval_usec = now.tv_usec; vblwait->reply.tval_usec = now.tv_usec;
DRM_DEBUG("returning %u to client\n", DRM_DEBUG("crtc %d returning %u to client\n",
vblwait->reply.sequence); pipe, vblwait->reply.sequence);
} else { } else {
DRM_DEBUG("vblank wait interrupted by signal\n"); DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe);
} }
done: done:
...@@ -1735,6 +1736,16 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) ...@@ -1735,6 +1736,16 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
wake_up(&vblank->queue); wake_up(&vblank->queue);
drm_handle_vblank_events(dev, pipe); 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); spin_unlock_irqrestore(&dev->event_lock, irqflags);
return true; return true;
......
/*
* 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, ...@@ -24,36 +24,36 @@ TRACE_EVENT(drm_vblank_event,
); );
TRACE_EVENT(drm_vblank_event_queued, TRACE_EVENT(drm_vblank_event_queued,
TP_PROTO(pid_t pid, int crtc, unsigned int seq), TP_PROTO(struct drm_file *file, int crtc, unsigned int seq),
TP_ARGS(pid, crtc, seq), TP_ARGS(file, crtc, seq),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(pid_t, pid) __field(struct drm_file *, file)
__field(int, crtc) __field(int, crtc)
__field(unsigned int, seq) __field(unsigned int, seq)
), ),
TP_fast_assign( TP_fast_assign(
__entry->pid = pid; __entry->file = file;
__entry->crtc = crtc; __entry->crtc = crtc;
__entry->seq = seq; __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) __entry->seq)
); );
TRACE_EVENT(drm_vblank_event_delivered, TRACE_EVENT(drm_vblank_event_delivered,
TP_PROTO(pid_t pid, int crtc, unsigned int seq), TP_PROTO(struct drm_file *file, int crtc, unsigned int seq),
TP_ARGS(pid, crtc, seq), TP_ARGS(file, crtc, seq),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(pid_t, pid) __field(struct drm_file *, file)
__field(int, crtc) __field(int, crtc)
__field(unsigned int, seq) __field(unsigned int, seq)
), ),
TP_fast_assign( TP_fast_assign(
__entry->pid = pid; __entry->file = file;
__entry->crtc = crtc; __entry->crtc = crtc;
__entry->seq = seq; __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) __entry->seq)
); );
......
...@@ -161,17 +161,7 @@ static void fsl_dcu_drm_lastclose(struct drm_device *dev) ...@@ -161,17 +161,7 @@ static void fsl_dcu_drm_lastclose(struct drm_device *dev)
drm_fbdev_cma_restore_mode(fsl_dev->fbdev); drm_fbdev_cma_restore_mode(fsl_dev->fbdev);
} }
static const struct file_operations fsl_dcu_drm_fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver fsl_dcu_drm_driver = { static struct drm_driver fsl_dcu_drm_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
......
...@@ -146,17 +146,7 @@ static int kirin_drm_kms_init(struct drm_device *dev) ...@@ -146,17 +146,7 @@ static int kirin_drm_kms_init(struct drm_device *dev)
return ret; return ret;
} }
static const struct file_operations kirin_drm_fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static int kirin_gem_cma_dumb_create(struct drm_file *file, static int kirin_gem_cma_dumb_create(struct drm_file *file,
struct drm_device *dev, struct drm_device *dev,
......
...@@ -53,16 +53,7 @@ static void imx_drm_driver_lastclose(struct drm_device *drm) ...@@ -53,16 +53,7 @@ static void imx_drm_driver_lastclose(struct drm_device *drm)
drm_fbdev_cma_restore_mode(imxdrm->fbhelper); drm_fbdev_cma_restore_mode(imxdrm->fbhelper);
} }
static const struct file_operations imx_drm_driver_fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
void imx_drm_connector_destroy(struct drm_connector *connector) void imx_drm_connector_destroy(struct drm_connector *connector)
{ {
......
...@@ -91,19 +91,7 @@ static irqreturn_t meson_irq(int irq, void *arg) ...@@ -91,19 +91,7 @@ static irqreturn_t meson_irq(int irq, void *arg)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static const struct file_operations fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver meson_driver = { static struct drm_driver meson_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
......
...@@ -311,6 +311,8 @@ void msm_perf_debugfs_cleanup(struct msm_drm_private *priv); ...@@ -311,6 +311,8 @@ void msm_perf_debugfs_cleanup(struct msm_drm_private *priv);
#else #else
static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; } 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_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 #endif
struct clk *msm_clk_get(struct platform_device *pdev, const char *name); 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) ...@@ -319,19 +319,7 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static const struct file_operations fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver mxsfb_driver = { static struct drm_driver mxsfb_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | .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) ...@@ -211,7 +211,6 @@ usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
goto done; goto done;
ntfy->p->base.event = &ntfy->p->e.base; ntfy->p->base.event = &ntfy->p->e.base;
ntfy->p->base.file_priv = f; 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.type = DRM_NOUVEAU_EVENT_NVIF;
ntfy->p->e.base.length = sizeof(ntfy->p->e.base) + ntfy->reply; 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, ...@@ -572,6 +572,8 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane,
ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd),
QXL_RELEASE_CURSOR_CMD, QXL_RELEASE_CURSOR_CMD,
&release, NULL); &release, NULL);
if (ret)
return;
cmd = (struct qxl_cursor_cmd *) qxl_release_map(qdev, release); cmd = (struct qxl_cursor_cmd *) qxl_release_map(qdev, release);
......
...@@ -226,17 +226,7 @@ static void rcar_du_lastclose(struct drm_device *dev) ...@@ -226,17 +226,7 @@ static void rcar_du_lastclose(struct drm_device *dev)
drm_fbdev_cma_restore_mode(rcdu->fbdev); drm_fbdev_cma_restore_mode(rcdu->fbdev);
} }
static const struct file_operations rcar_du_fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver rcar_du_driver = { static struct drm_driver rcar_du_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME
......
...@@ -94,7 +94,7 @@ static int cdn_dp_grf_write(struct cdn_dp_device *dp, ...@@ -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) static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
{ {
int ret; int ret;
u32 rate; unsigned long rate;
ret = clk_prepare_enable(dp->pclk); ret = clk_prepare_enable(dp->pclk);
if (ret < 0) { if (ret < 0) {
...@@ -123,7 +123,8 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp) ...@@ -123,7 +123,8 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
rate = clk_get_rate(dp->core_clk); rate = clk_get_rate(dp->core_clk);
if (!rate) { 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; goto err_set_rate;
} }
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
#define LINK_TRAINING_RETRY_MS 20 #define LINK_TRAINING_RETRY_MS 20
#define LINK_TRAINING_TIMEOUT_MS 500 #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); writel(clk / 1000000, dp->regs + SW_CLK_H);
} }
...@@ -671,6 +671,10 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) ...@@ -671,6 +671,10 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
rem = do_div(symbol, 1000); rem = do_div(symbol, 1000);
if (tu_size_reg > 64) { if (tu_size_reg > 64) {
ret = -EINVAL; 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; goto err_config_video;
} }
} while ((symbol <= 1) || (tu_size_reg - symbol < 4) || } while ((symbol <= 1) || (tu_size_reg - symbol < 4) ||
......
...@@ -121,12 +121,11 @@ ...@@ -121,12 +121,11 @@
/* dptx phy addr */ /* dptx phy addr */
#define DP_TX_PHY_CONFIG_REG 0x2000 #define DP_TX_PHY_CONFIG_REG 0x2000
#define DP_TX_PHY_STATUS_REG 0x2004 #define DP_TX_PHY_SW_RESET 0x2004
#define DP_TX_PHY_SW_RESET 0x2008 #define DP_TX_PHY_SCRAMBLER_SEED 0x2008
#define DP_TX_PHY_SCRAMBLER_SEED 0x200c #define DP_TX_PHY_TRAINING_01_04 0x200c
#define DP_TX_PHY_TRAINING_01_04 0x2010 #define DP_TX_PHY_TRAINING_05_08 0x2010
#define DP_TX_PHY_TRAINING_05_08 0x2014 #define DP_TX_PHY_TRAINING_09_10 0x2014
#define DP_TX_PHY_TRAINING_09_10 0x2018
#define TEST_COR 0x23fc #define TEST_COR 0x23fc
/* dptx hpd addr */ /* dptx hpd addr */
...@@ -462,7 +461,7 @@ enum vic_bt_type { ...@@ -462,7 +461,7 @@ enum vic_bt_type {
void cdn_dp_clock_reset(struct cdn_dp_device *dp); 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, int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
u32 i_size, const u32 *d_mem, u32 d_size); u32 i_size, const u32 *d_mem, u32 d_size);
int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable); 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) ...@@ -127,17 +127,7 @@ static irqreturn_t shmob_drm_irq(int irq, void *arg)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static const struct file_operations shmob_drm_fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver shmob_drm_driver = { static struct drm_driver shmob_drm_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
......
...@@ -167,16 +167,7 @@ static void sti_mode_config_init(struct drm_device *dev) ...@@ -167,16 +167,7 @@ static void sti_mode_config_init(struct drm_device *dev)
dev->mode_config.funcs = &sti_mode_config_funcs; dev->mode_config.funcs = &sti_mode_config_funcs;
} }
static const struct file_operations sti_driver_fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver sti_driver = { static struct drm_driver sti_driver = {
.driver_features = DRIVER_MODESET | .driver_features = DRIVER_MODESET |
......
...@@ -25,17 +25,7 @@ ...@@ -25,17 +25,7 @@
#include "sun4i_framebuffer.h" #include "sun4i_framebuffer.h"
#include "sun4i_layer.h" #include "sun4i_layer.h"
static const struct file_operations sun4i_drv_fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver sun4i_drv_driver = { static struct drm_driver sun4i_drv_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
......
...@@ -529,17 +529,7 @@ static int tilcdc_debugfs_init(struct drm_minor *minor) ...@@ -529,17 +529,7 @@ static int tilcdc_debugfs_init(struct drm_minor *minor)
} }
#endif #endif
static const struct file_operations fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver tilcdc_driver = { static struct drm_driver tilcdc_driver = {
.driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | .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, ...@@ -590,7 +590,7 @@ static int mipi_dbi_spi1e_transfer(struct mipi_dbi *mipi, int dc,
ret = spi_sync(spi, &m); ret = spi_sync(spi, &m);
if (ret) if (ret)
return ret; return ret;
}; }
return 0; return 0;
} }
...@@ -654,7 +654,7 @@ static int mipi_dbi_spi1_transfer(struct mipi_dbi *mipi, int dc, ...@@ -654,7 +654,7 @@ static int mipi_dbi_spi1_transfer(struct mipi_dbi *mipi, int dc,
ret = spi_sync(spi, &m); ret = spi_sync(spi, &m);
if (ret) if (ret)
return ret; return ret;
}; }
return 0; return 0;
} }
......
...@@ -2,11 +2,15 @@ config DRM_VC4 ...@@ -2,11 +2,15 @@ config DRM_VC4
tristate "Broadcom VC4 Graphics" tristate "Broadcom VC4 Graphics"
depends on ARCH_BCM2835 || COMPILE_TEST depends on ARCH_BCM2835 || COMPILE_TEST
depends on DRM depends on DRM
depends on SND && SND_SOC
depends on COMMON_CLK depends on COMMON_CLK
select DRM_KMS_HELPER select DRM_KMS_HELPER
select DRM_KMS_CMA_HELPER select DRM_KMS_CMA_HELPER
select DRM_GEM_CMA_HELPER select DRM_GEM_CMA_HELPER
select DRM_PANEL select DRM_PANEL
select SND_PCM
select SND_PCM_ELD
select SND_SOC_GENERIC_DMAENGINE_PCM
select DRM_MIPI_DSI select DRM_MIPI_DSI
help help
Choose this option if you have a system that has a Broadcom Choose this option if you have a system that has a Broadcom
......
...@@ -349,26 +349,20 @@ static struct platform_driver vc4_platform_driver = { ...@@ -349,26 +349,20 @@ static struct platform_driver vc4_platform_driver = {
static int __init vc4_drm_register(void) 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); return platform_driver_register(&vc4_platform_driver);
} }
static void __exit vc4_drm_unregister(void) static void __exit vc4_drm_unregister(void)
{ {
int i; platform_unregister_drivers(component_drivers,
ARRAY_SIZE(component_drivers));
for (i = ARRAY_SIZE(component_drivers) - 1; i >= 0; i--)
platform_driver_unregister(component_drivers[i]);
platform_driver_unregister(&vc4_platform_driver); platform_driver_unregister(&vc4_platform_driver);
} }
......
...@@ -48,11 +48,27 @@ ...@@ -48,11 +48,27 @@
#include "linux/clk.h" #include "linux/clk.h"
#include "linux/component.h" #include "linux/component.h"
#include "linux/i2c.h" #include "linux/i2c.h"
#include "linux/of_address.h"
#include "linux/of_gpio.h" #include "linux/of_gpio.h"
#include "linux/of_platform.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_drv.h"
#include "vc4_regs.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. */ /* General HDMI hardware state. */
struct vc4_hdmi { struct vc4_hdmi {
struct platform_device *pdev; struct platform_device *pdev;
...@@ -60,6 +76,8 @@ struct vc4_hdmi { ...@@ -60,6 +76,8 @@ struct vc4_hdmi {
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_connector *connector; struct drm_connector *connector;
struct vc4_hdmi_audio audio;
struct i2c_adapter *ddc; struct i2c_adapter *ddc;
void __iomem *hdmicore_regs; void __iomem *hdmicore_regs;
void __iomem *hd_regs; void __iomem *hd_regs;
...@@ -115,6 +133,10 @@ static const struct { ...@@ -115,6 +133,10 @@ static const struct {
HDMI_REG(VC4_HDMI_SW_RESET_CONTROL), HDMI_REG(VC4_HDMI_SW_RESET_CONTROL),
HDMI_REG(VC4_HDMI_HOTPLUG_INT), HDMI_REG(VC4_HDMI_HOTPLUG_INT),
HDMI_REG(VC4_HDMI_HOTPLUG), 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_RAM_PACKET_CONFIG),
HDMI_REG(VC4_HDMI_HORZA), HDMI_REG(VC4_HDMI_HORZA),
HDMI_REG(VC4_HDMI_HORZB), HDMI_REG(VC4_HDMI_HORZB),
...@@ -125,6 +147,7 @@ static const struct { ...@@ -125,6 +147,7 @@ static const struct {
HDMI_REG(VC4_HDMI_VERTB0), HDMI_REG(VC4_HDMI_VERTB0),
HDMI_REG(VC4_HDMI_VERTB1), HDMI_REG(VC4_HDMI_VERTB1),
HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL), HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL),
HDMI_REG(VC4_HDMI_TX_PHY_CTL0),
}; };
static const struct { static const struct {
...@@ -133,6 +156,9 @@ static const struct { ...@@ -133,6 +156,9 @@ static const struct {
} hd_regs[] = { } hd_regs[] = {
HDMI_REG(VC4_HD_M_CTL), HDMI_REG(VC4_HD_M_CTL),
HDMI_REG(VC4_HD_MAI_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_VID_CTL),
HDMI_REG(VC4_HD_CSC_CTL), HDMI_REG(VC4_HD_CSC_CTL),
HDMI_REG(VC4_HD_FRAME_COUNT), HDMI_REG(VC4_HD_FRAME_COUNT),
...@@ -232,6 +258,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) ...@@ -232,6 +258,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
drm_mode_connector_update_edid_property(connector, edid); drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid); ret = drm_add_edid_modes(connector, edid);
drm_edid_to_eld(connector, edid);
return ret; return ret;
} }
...@@ -317,7 +344,7 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, ...@@ -317,7 +344,7 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_dev *vc4 = to_vc4_dev(dev);
u32 packet_id = frame->any.type - 0x80; 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]; uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
ssize_t len, i; ssize_t len, i;
int ret; int ret;
...@@ -398,6 +425,24 @@ static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder) ...@@ -398,6 +425,24 @@ static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
vc4_hdmi_write_infoframe(encoder, &frame); 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) static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
{ {
vc4_hdmi_set_avi_infoframe(encoder); vc4_hdmi_set_avi_infoframe(encoder);
...@@ -606,6 +651,447 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { ...@@ -606,6 +651,447 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
.enable = vc4_hdmi_encoder_enable, .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) static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
{ {
struct platform_device *pdev = to_platform_device(dev); 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) ...@@ -737,6 +1223,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
goto err_destroy_encoder; goto err_destroy_encoder;
} }
ret = vc4_hdmi_audio_init(hdmi);
if (ret)
goto err_destroy_encoder;
return 0; return 0;
err_destroy_encoder: err_destroy_encoder:
...@@ -758,6 +1248,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, ...@@ -758,6 +1248,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
struct vc4_dev *vc4 = drm->dev_private; struct vc4_dev *vc4 = drm->dev_private;
struct vc4_hdmi *hdmi = vc4->hdmi; struct vc4_hdmi *hdmi = vc4->hdmi;
vc4_hdmi_audio_cleanup(hdmi);
vc4_hdmi_connector_destroy(hdmi->connector); vc4_hdmi_connector_destroy(hdmi->connector);
vc4_hdmi_encoder_destroy(hdmi->encoder); vc4_hdmi_encoder_destroy(hdmi->encoder);
......
...@@ -842,10 +842,8 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, ...@@ -842,10 +842,8 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane), vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
GFP_KERNEL); GFP_KERNEL);
if (!vc4_plane) { if (!vc4_plane)
ret = -ENOMEM; return ERR_PTR(-ENOMEM);
goto fail;
}
for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
/* Don't allow YUV in cursor planes, since that means /* Don't allow YUV in cursor planes, since that means
...@@ -866,9 +864,4 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, ...@@ -866,9 +864,4 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
drm_plane_helper_add(plane, &vc4_plane_helper_funcs); drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
return plane; return plane;
fail:
if (plane)
vc4_plane_destroy(plane);
return ERR_PTR(ret);
} }
...@@ -446,11 +446,62 @@ ...@@ -446,11 +446,62 @@
#define VC4_HDMI_HOTPLUG 0x00c #define VC4_HDMI_HOTPLUG 0x00c
# define VC4_HDMI_HOTPLUG_CONNECTED BIT(0) # 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_CONFIG 0x0a0
# define VC4_HDMI_RAM_PACKET_ENABLE BIT(16) # define VC4_HDMI_RAM_PACKET_ENABLE BIT(16)
#define VC4_HDMI_RAM_PACKET_STATUS 0x0a4 #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 0x0c4
# define VC4_HDMI_HORZA_VPOS BIT(14) # define VC4_HDMI_HORZA_VPOS BIT(14)
# define VC4_HDMI_HORZA_HPOS BIT(13) # define VC4_HDMI_HORZA_HPOS BIT(13)
...@@ -512,7 +563,11 @@ ...@@ -512,7 +563,11 @@
#define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 #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_HDMI_PACKET_STRIDE 0x24
#define VC4_HD_M_CTL 0x00c #define VC4_HD_M_CTL 0x00c
...@@ -522,6 +577,56 @@ ...@@ -522,6 +577,56 @@
# define VC4_HD_M_ENABLE BIT(0) # define VC4_HD_M_ENABLE BIT(0)
#define VC4_HD_MAI_CTL 0x014 #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 0x038
# define VC4_HD_VID_CTL_ENABLE BIT(31) # define VC4_HD_VID_CTL_ENABLE BIT(31)
......
...@@ -104,7 +104,7 @@ static int vgem_open(struct drm_device *dev, struct drm_file *file) ...@@ -104,7 +104,7 @@ static int vgem_open(struct drm_device *dev, struct drm_file *file)
return 0; 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; struct vgem_file *vfile = file->driver_priv;
...@@ -303,7 +303,7 @@ static int vgem_prime_mmap(struct drm_gem_object *obj, ...@@ -303,7 +303,7 @@ static int vgem_prime_mmap(struct drm_gem_object *obj,
static struct drm_driver vgem_driver = { static struct drm_driver vgem_driver = {
.driver_features = DRIVER_GEM | DRIVER_PRIME, .driver_features = DRIVER_GEM | DRIVER_PRIME,
.open = vgem_open, .open = vgem_open,
.preclose = vgem_preclose, .postclose = vgem_postclose,
.gem_free_object_unlocked = vgem_gem_free_object, .gem_free_object_unlocked = vgem_gem_free_object,
.gem_vm_ops = &vgem_gem_vm_ops, .gem_vm_ops = &vgem_gem_vm_ops,
.ioctls = vgem_ioctls, .ioctls = vgem_ioctls,
......
...@@ -97,8 +97,8 @@ virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev, ...@@ -97,8 +97,8 @@ virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf; struct virtio_gpu_vbuffer *vbuf;
vbuf = kmem_cache_alloc(vgdev->vbufs, GFP_KERNEL); vbuf = kmem_cache_alloc(vgdev->vbufs, GFP_KERNEL);
if (IS_ERR(vbuf)) if (!vbuf)
return ERR_CAST(vbuf); return ERR_PTR(-ENOMEM);
memset(vbuf, 0, VBUFFER_SIZE); memset(vbuf, 0, VBUFFER_SIZE);
BUG_ON(size > MAX_INLINE_CMD_SIZE); BUG_ON(size > MAX_INLINE_CMD_SIZE);
......
...@@ -53,19 +53,7 @@ static void zx_drm_lastclose(struct drm_device *drm) ...@@ -53,19 +53,7 @@ static void zx_drm_lastclose(struct drm_device *drm)
drm_fbdev_cma_restore_mode(priv->fbdev); drm_fbdev_cma_restore_mode(priv->fbdev);
} }
static const struct file_operations zx_drm_fops = { DEFINE_DRM_GEM_CMA_FOPS(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,
};
static struct drm_driver zx_drm_driver = { static struct drm_driver zx_drm_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
......
...@@ -87,6 +87,53 @@ enum subpixel_order { ...@@ -87,6 +87,53 @@ enum subpixel_order {
SubPixelVerticalRGB, SubPixelVerticalRGB,
SubPixelVerticalBGR, SubPixelVerticalBGR,
SubPixelNone, 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 { ...@@ -204,6 +251,11 @@ struct drm_display_info {
* @cea_rev: CEA revision of the HDMI sink. * @cea_rev: CEA revision of the HDMI sink.
*/ */
u8 cea_rev; 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, int drm_display_info_set_bus_formats(struct drm_display_info *info,
......
...@@ -64,7 +64,6 @@ struct drm_mode_create_dumb; ...@@ -64,7 +64,6 @@ struct drm_mode_create_dumb;
* structure for GEM drivers. * structure for GEM drivers.
*/ */
struct drm_driver { struct drm_driver {
/** /**
* @load: * @load:
* *
...@@ -76,14 +75,94 @@ struct drm_driver { ...@@ -76,14 +75,94 @@ struct drm_driver {
* See drm_dev_init() and drm_dev_register() for proper and * See drm_dev_init() and drm_dev_register() for proper and
* race-free way to set up a &struct drm_device. * race-free way to set up a &struct drm_device.
* *
* This is deprecated, do not use!
*
* Returns: * Returns:
* *
* Zero on success, non-zero value on failure. * Zero on success, non-zero value on failure.
*/ */
int (*load) (struct drm_device *, unsigned long flags); 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 *); 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); 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 *); 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 *); void (*lastclose) (struct drm_device *);
/** /**
......
...@@ -476,5 +476,4 @@ void drm_edid_get_monitor_name(struct edid *edid, char *name, ...@@ -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, struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
int hsize, int vsize, int fresh, int hsize, int vsize, int fresh,
bool rb); bool rb);
#endif /* __DRM_EDID_H__ */ #endif /* __DRM_EDID_H__ */
...@@ -45,6 +45,7 @@ struct drm_device; ...@@ -45,6 +45,7 @@ struct drm_device;
* FIXME: Not sure we want to have drm_minor here in the end, but to avoid * 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. * header include loops we need it here for now.
*/ */
enum drm_minor_type { enum drm_minor_type {
DRM_MINOR_PRIMARY, DRM_MINOR_PRIMARY,
DRM_MINOR_CONTROL, DRM_MINOR_CONTROL,
...@@ -52,12 +53,19 @@ enum drm_minor_type { ...@@ -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 { struct drm_minor {
int index; /**< Minor device number */ /* private: */
int type; /**< Control or render */ int index; /* Minor device number */
struct device *kdev; /**< Linux device */ int type; /* Control or render */
struct device *kdev; /* Linux device */
struct drm_device *dev; struct drm_device *dev;
struct dentry *debugfs_root; struct dentry *debugfs_root;
...@@ -66,91 +74,286 @@ struct drm_minor { ...@@ -66,91 +74,286 @@ struct drm_minor {
struct mutex debugfs_lock; /* Protects debugfs_list. */ 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 { 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; 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); 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; 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 dma_fence *fence;
/**
* @file_priv:
*
* &struct drm_file where @event should be delivered to. Only set when
* @event is set.
*/
struct drm_file *file_priv;
/**
* @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; 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; struct list_head pending_link;
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 */
}; };
/** File private data */ /**
* struct drm_file - DRM file private data
*
* This structure tracks DRM state per open file descriptor.
*/
struct drm_file { 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; 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; 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; unsigned universal_planes:1;
/* true if client understands atomic properties */
/** @atomic: True if client understands atomic properties. */
unsigned atomic:1; 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; unsigned is_master:1;
/**
* @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;
/** @pid: Process that opened this file. */
struct pid *pid; struct pid *pid;
/** @magic: Authentication magic, see @authenticated. */
drm_magic_t magic; 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; struct list_head lhead;
/** @minor: &struct drm_minor for this file. */
struct drm_minor *minor; struct drm_minor *minor;
unsigned long lock_count;
/** Mapping of mm object handles to object pointers. */ /**
* @object_idr:
*
* Mapping of mm object handles to object pointers. Used by the GEM
* subsystem. Protected by @table_lock.
*/
struct idr object_idr; struct idr object_idr;
/** Lock for synchronization of access to object_idr. */
/** @table_lock: Protects @object_idr. */
spinlock_t table_lock; spinlock_t table_lock;
/** @filp: Pointer to the core file structure. */
struct file *filp; 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; 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:
*
* 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 * Protected by @fbs_lock. Note that the @fbs list holds a reference on
* the fb object to prevent it from untimely disappearing. * the framebuffer object to prevent it from untimely disappearing.
*/ */
struct list_head fbs; struct list_head fbs;
/** @fbs_lock: Protects @fbs. */
struct mutex fbs_lock; 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; struct list_head blobs;
/** @event_wait: Waitqueue for new events added to @event_list. */
wait_queue_head_t event_wait; 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; 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; 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; int event_space;
/** @event_read_lock: Serializes drm_read(). */
struct mutex event_read_lock; struct mutex event_read_lock;
/**
* @prime:
*
* Per-file buffer caches used by the PRIME buffer sharing code.
*/
struct drm_prime_file_private prime; 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) static inline bool drm_is_render_client(const struct drm_file *file_priv)
{ {
return file_priv->minor->type == DRM_MINOR_RENDER; 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) static inline bool drm_is_control_client(const struct drm_file *file_priv)
{ {
return file_priv->minor->type == DRM_MINOR_CONTROL; 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); int drm_open(struct inode *inode, struct file *filp);
ssize_t drm_read(struct file *filp, char __user *buffer, ssize_t drm_read(struct file *filp, char __user *buffer,
size_t count, loff_t *offset); size_t count, loff_t *offset);
......
...@@ -178,6 +178,32 @@ struct drm_gem_object { ...@@ -178,6 +178,32 @@ struct drm_gem_object {
struct dma_buf_attachment *import_attach; 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_release(struct drm_gem_object *obj);
void drm_gem_object_free(struct kref *kref); void drm_gem_object_free(struct kref *kref);
int drm_gem_object_init(struct drm_device *dev, int drm_gem_object_init(struct drm_device *dev,
......
...@@ -26,6 +26,32 @@ to_drm_gem_cma_obj(struct drm_gem_object *gem_obj) ...@@ -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); 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 */ /* free GEM object */
void drm_gem_cma_free_object(struct drm_gem_object *gem_obj); void drm_gem_cma_free_object(struct drm_gem_object *gem_obj);
......
/*
* 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, ...@@ -83,4 +83,6 @@ struct dma_fence_array *dma_fence_array_create(int num_fences,
u64 context, unsigned seqno, u64 context, unsigned seqno,
bool signal_on_any); bool signal_on_any);
bool dma_fence_match_context(struct dma_fence *fence, u64 context);
#endif /* __LINUX_DMA_FENCE_ARRAY_H */ #endif /* __LINUX_DMA_FENCE_ARRAY_H */
...@@ -35,6 +35,7 @@ enum hdmi_infoframe_type { ...@@ -35,6 +35,7 @@ enum hdmi_infoframe_type {
}; };
#define HDMI_IEEE_OUI 0x000c03 #define HDMI_IEEE_OUI 0x000c03
#define HDMI_FORUM_IEEE_OUI 0xc45dd8
#define HDMI_INFOFRAME_HEADER_SIZE 4 #define HDMI_INFOFRAME_HEADER_SIZE 4
#define HDMI_AVI_INFOFRAME_SIZE 13 #define HDMI_AVI_INFOFRAME_SIZE 13
#define HDMI_SPD_INFOFRAME_SIZE 25 #define HDMI_SPD_INFOFRAME_SIZE 25
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment