From 49978be705dd7c202f58bb401ce82bdca6cf9756 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 6 Mar 2017 11:13:51 -0800 Subject: [PATCH 01/37] docs: Clarify details for reporting security bugs The kernel security team is regularly asked to provide CVE identifiers, which we don't normally do. This updates the documentation to mention this and adds some more details about coordination and patch handling that come up regularly. Based on an earlier draft by Willy Tarreau. Signed-off-by: Kees Cook Acked-by: Willy Tarreau Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/security-bugs.rst | 39 ++++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/Documentation/admin-guide/security-bugs.rst b/Documentation/admin-guide/security-bugs.rst index 4f7414cad5861..47574b382d758 100644 --- a/Documentation/admin-guide/security-bugs.rst +++ b/Documentation/admin-guide/security-bugs.rst @@ -14,14 +14,17 @@ Contact The Linux kernel security team can be contacted by email at . This is a private list of security officers who will help verify the bug report and develop and release a fix. -It is possible that the security team will bring in extra help from -area maintainers to understand and fix the security vulnerability. +If you already have a fix, please include it with your report, as +that can speed up the process considerably. It is possible that the +security team will bring in extra help from area maintainers to +understand and fix the security vulnerability. As it is with any bug, the more information provided the easier it will be to diagnose and fix. Please review the procedure outlined in -admin-guide/reporting-bugs.rst if you are unclear about what information is helpful. -Any exploit code is very helpful and will not be released without -consent from the reporter unless it has already been made public. +admin-guide/reporting-bugs.rst if you are unclear about what +information is helpful. Any exploit code is very helpful and will not +be released without consent from the reporter unless it has already been +made public. Disclosure ---------- @@ -39,6 +42,32 @@ disclosure is from immediate (esp. if it's already publicly known) to a few weeks. As a basic default policy, we expect report date to disclosure date to be on the order of 7 days. +Coordination +------------ + +Fixes for sensitive bugs, such as those that might lead to privilege +escalations, may need to be coordinated with the private + 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: + + +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 ------------------------- From b742fd6b0c701bdb2697a83f0acf8a702eb545c8 Mon Sep 17 00:00:00 2001 From: sayli karnik Date: Thu, 9 Mar 2017 11:46:22 +0530 Subject: [PATCH 02/37] Documentation: phy: Fix repetition of word 'the' The patch replaces 'the the' with 'the' in the documantation. Signed-off-by: sayli karnik Signed-off-by: Jonathan Corbet --- Documentation/phy.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/phy.txt b/Documentation/phy.txt index 0aa994bd9a917..383cdd863f083 100644 --- a/Documentation/phy.txt +++ b/Documentation/phy.txt @@ -97,7 +97,7 @@ should contain the phy name as given in the dt data and in the case of non-dt boot, it should contain the label of the PHY. The two devm_phy_get associates the device with the PHY using devres on successful PHY get. On driver detach, release function is invoked on -the the devres data and devres data is freed. phy_optional_get and +the devres data and devres data is freed. phy_optional_get and devm_phy_optional_get should be used when the phy is optional. These two functions will never return -ENODEV, but instead returns NULL when the phy cannot be found.Some generic drivers, such as ehci, may use multiple From 88486bee065ace1aa9cdd6fa48586ef3f2d62ec9 Mon Sep 17 00:00:00 2001 From: sayli karnik Date: Thu, 9 Mar 2017 11:47:07 +0530 Subject: [PATCH 03/37] Documentation: ABI: testing: sysfs-bus-pci: Fix repetition of word 'the' The patch replaces 'the the' with 'the' in the documentation. Signed-off-by: sayli karnik Signed-off-by: Jonathan Corbet --- Documentation/ABI/testing/sysfs-bus-pci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 5a1732b787071..e4e90104d7c38 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -299,5 +299,5 @@ What: /sys/bus/pci/devices/.../revision Date: November 2016 Contact: Emil Velikov 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. From 54f5d13b708a64177d4437a2b23fb05035f90716 Mon Sep 17 00:00:00 2001 From: sayli karnik Date: Thu, 9 Mar 2017 11:48:21 +0530 Subject: [PATCH 04/37] Documentation: cpu-freq: cpu-drivers: Fix repetition of word 'to' The patch replaces 'to to' with 'to' in the documentation. Signed-off-by: sayli karnik Acked-by: Viresh Kumar Signed-off-by: Jonathan Corbet --- Documentation/cpu-freq/cpu-drivers.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/cpu-freq/cpu-drivers.txt b/Documentation/cpu-freq/cpu-drivers.txt index f71e6be26b83b..434c49cc7330a 100644 --- a/Documentation/cpu-freq/cpu-drivers.txt +++ b/Documentation/cpu-freq/cpu-drivers.txt @@ -231,7 +231,7 @@ the reference implementation in drivers/cpufreq/longrun.c Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset. get_intermediate should return a stable intermediate frequency platform wants to -switch to, and target_intermediate() should set CPU to to that frequency, before +switch to, and target_intermediate() should set CPU to that frequency, before jumping to the frequency corresponding to 'index'. Core will take care of sending notifications and driver doesn't have to handle them in target_intermediate() or target_index(). From c3c53600f62b5fa92acf3a7f5eb71bacb25f6b01 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 2 Mar 2017 16:16:33 +0100 Subject: [PATCH 05/37] doc: Explain light-handed markup preference a bit better We're still pretty far away from anything like a consensus, but there's clearly a lot of people who prefer an as-light as possible approach to converting existing .txt files to .rst. Make sure this is properly taken into account and clear. Motivated by discussions with Peter and Christoph and others. Cc: Christoph Hellwig Cc: Peter Zijlstra Cc: Jani Nikula Cc: Mauro Carvalho Chehab Signed-off-by: Daniel Vetter Signed-off-by: Jonathan Corbet --- Documentation/doc-guide/sphinx.rst | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Documentation/doc-guide/sphinx.rst b/Documentation/doc-guide/sphinx.rst index 96fe7ccb2c670..532d65b70500e 100644 --- a/Documentation/doc-guide/sphinx.rst +++ b/Documentation/doc-guide/sphinx.rst @@ -73,7 +73,16 @@ Specific guidelines for the kernel documentation Here are some specific guidelines for the kernel documentation: -* Please don't go overboard with reStructuredText markup. Keep it simple. +* Please don't go overboard with reStructuredText markup. Keep it + simple. For the most part the documentation should be plain text with + just enough consistency in formatting that it can be converted to + other formats. + +* Please keep the formatting changes minimal when converting existing + documentation to reStructuredText. + +* Also update the content, not just the formatting, when converting + documentation. * Please stick to this order of heading adornments: @@ -103,6 +112,12 @@ Here are some specific guidelines for the kernel documentation: the order as encountered."), having the higher levels the same overall makes it easier to follow the documents. +* For inserting fixed width text blocks (for code examples, use case + examples, etc.), use ``::`` for anything that doesn't really benefit + from syntax highlighting, especially short snippets. Use + ``.. code-block:: `` for longer code blocks that benefit + from highlighting. + the C domain ------------ From db6ccf23e8ba40fc2e8914ec9c0eb950df71d9fe Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Mon, 6 Mar 2017 14:09:27 +0100 Subject: [PATCH 06/37] docs-rst: automatically convert Graphviz and SVG images This patch brings scalable figure, image handling and a concept to embed *render* markups: * DOT (http://www.graphviz.org) * SVG For image handling use the 'image' replacement:: .. kernel-image:: svg_image.svg :alt: simple SVG image For figure handling use the 'figure' replacement:: .. kernel-figure:: svg_image.svg :alt: simple SVG image SVG image example Embed *render* markups (or languages) like Graphviz's **DOT** is provided by the *render* directive.:: .. kernel-render:: DOT :alt: foobar digraph :caption: Embedded **DOT** (Graphviz) code. digraph foo { "bar" -> "baz"; } The *render* directive is a concept to integrate *render* markups and languages, yet supported markups: * DOT: render embedded Graphviz's **DOT** * SVG: render embedded Scalable Vector Graphics (**SVG**) Cc: Jani Nikula Cc: Laurent Pinchart Tested-by: Mauro Carvalho Chehab Tested-by: Daniel Vetter Signed-off-by: Daniel Vetter (v2 - v5) Signed-off-by: Markus Heiser (v1, v6) Signed-off-by: Jonathan Corbet --- Documentation/conf.py | 2 +- Documentation/doc-guide/hello.dot | 3 + Documentation/doc-guide/sphinx.rst | 98 ++++- Documentation/doc-guide/svg_image.svg | 10 + Documentation/process/changes.rst | 7 +- Documentation/sphinx/kfigure.py | 551 ++++++++++++++++++++++++++ 6 files changed, 665 insertions(+), 6 deletions(-) create mode 100644 Documentation/doc-guide/hello.dot create mode 100644 Documentation/doc-guide/svg_image.svg create mode 100644 Documentation/sphinx/kfigure.py diff --git a/Documentation/conf.py b/Documentation/conf.py index 7fadb3b832934..f2b9161583773 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -34,7 +34,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include', 'cdomain'] +extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include', 'cdomain', 'kfigure'] # The name of the math extension changed on Sphinx 1.4 if major == 1 and minor > 3: diff --git a/Documentation/doc-guide/hello.dot b/Documentation/doc-guide/hello.dot new file mode 100644 index 0000000000000..504621dfc595e --- /dev/null +++ b/Documentation/doc-guide/hello.dot @@ -0,0 +1,3 @@ +graph G { + Hello -- World +} diff --git a/Documentation/doc-guide/sphinx.rst b/Documentation/doc-guide/sphinx.rst index 532d65b70500e..731334de3efdd 100644 --- a/Documentation/doc-guide/sphinx.rst +++ b/Documentation/doc-guide/sphinx.rst @@ -34,8 +34,9 @@ format-specific subdirectories under ``Documentation/output``. To generate documentation, Sphinx (``sphinx-build``) must obviously be installed. For prettier HTML output, the Read the Docs Sphinx theme -(``sphinx_rtd_theme``) is used if available. For PDF output, ``rst2pdf`` is also -needed. All of these are widely available and packaged in distributions. +(``sphinx_rtd_theme``) is used if available. For PDF output you'll also need +``XeLaTeX`` and ``convert(1)`` from ImageMagick (https://www.imagemagick.org). +All of these are widely available and packaged in distributions. To pass extra options to Sphinx, you can use the ``SPHINXOPTS`` make variable. For example, use ``make SPHINXOPTS=-v htmldocs`` to get more verbose @@ -232,3 +233,96 @@ Rendered as: * .. _`last row`: - column 3 + + +Figures & Images +================ + +If you want to add an image, you should use the ``kernel-figure`` and +``kernel-image`` directives. E.g. to insert a figure with a scalable +image format use SVG (:ref:`svg_image_example`):: + + .. kernel-figure:: svg_image.svg + :alt: simple SVG image + + SVG image example + +.. _svg_image_example: + +.. kernel-figure:: svg_image.svg + :alt: simple SVG image + + SVG image example + +The kernel figure (and image) directive support **DOT** formated files, see + +* DOT: http://graphviz.org/pdf/dotguide.pdf +* Graphviz: http://www.graphviz.org/content/dot-language + +A simple example (:ref:`hello_dot_file`):: + + .. kernel-figure:: hello.dot + :alt: hello world + + DOT's hello world example + +.. _hello_dot_file: + +.. kernel-figure:: hello.dot + :alt: hello world + + DOT's hello world example + +Embed *render* markups (or languages) like Graphviz's **DOT** is provided by the +``kernel-render`` directives.:: + + .. kernel-render:: DOT + :alt: foobar digraph + :caption: Embedded **DOT** (Graphviz) code + + digraph foo { + "bar" -> "baz"; + } + +How this will be rendered depends on the installed tools. If Graphviz is +installed, you will see an vector image. If not the raw markup is inserted as +*literal-block* (:ref:`hello_dot_render`). + +.. _hello_dot_render: + +.. kernel-render:: DOT + :alt: foobar digraph + :caption: Embedded **DOT** (Graphviz) code + + digraph foo { + "bar" -> "baz"; + } + +The *render* directive has all the options known from the *figure* directive, +plus option ``caption``. If ``caption`` has a value, a *figure* node is +inserted. If not, a *image* node is inserted. A ``caption`` is also needed, if +you want to refer it (:ref:`hello_svg_render`). + +Embedded **SVG**:: + + .. kernel-render:: SVG + :caption: Embedded **SVG** markup + :alt: so-nw-arrow + + + + ... + + +.. _hello_svg_render: + +.. kernel-render:: SVG + :caption: Embedded **SVG** markup + :alt: so-nw-arrow + + + + + + diff --git a/Documentation/doc-guide/svg_image.svg b/Documentation/doc-guide/svg_image.svg new file mode 100644 index 0000000000000..5405f85b81378 --- /dev/null +++ b/Documentation/doc-guide/svg_image.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst index 56ce661146656..e4f25038ef658 100644 --- a/Documentation/process/changes.rst +++ b/Documentation/process/changes.rst @@ -318,9 +318,10 @@ PDF outputs, it is recommended to use version 1.4.6. .. note:: Please notice that, for PDF and LaTeX output, you'll also need ``XeLaTeX`` - version 3.14159265. Depending on the distribution, you may also need - to install a series of ``texlive`` packages that provide the minimal - set of functionalities required for ``XeLaTex`` to work. + version 3.14159265. Depending on the distribution, you may also need to + install a series of ``texlive`` packages that provide the minimal set of + functionalities required for ``XeLaTex`` to work. For PDF output you'll also + need ``convert(1)`` from ImageMagick (https://www.imagemagick.org). Other tools ----------- diff --git a/Documentation/sphinx/kfigure.py b/Documentation/sphinx/kfigure.py new file mode 100644 index 0000000000000..cef4ad19624c5 --- /dev/null +++ b/Documentation/sphinx/kfigure.py @@ -0,0 +1,551 @@ +# -*- coding: utf-8; mode: python -*- +# pylint: disable=C0103, R0903, R0912, R0915 +u""" + scalable figure and image handling + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Sphinx extension which implements scalable image handling. + + :copyright: Copyright (C) 2016 Markus Heiser + :license: GPL Version 2, June 1991 see Linux/COPYING for details. + + The build for image formats depend on image's source format and output's + destination format. This extension implement methods to simplify image + handling from the author's POV. Directives like ``kernel-figure`` implement + methods *to* always get the best output-format even if some tools are not + installed. For more details take a look at ``convert_image(...)`` which is + the core of all conversions. + + * ``.. kernel-image``: for image handling / a ``.. image::`` replacement + + * ``.. kernel-figure``: for figure handling / a ``.. figure::`` replacement + + * ``.. kernel-render``: for render markup / a concept to embed *render* + markups (or languages). Supported markups (see ``RENDER_MARKUP_EXT``) + + - ``DOT``: render embedded Graphviz's **DOC** + - ``SVG``: render embedded Scalable Vector Graphics (**SVG**) + - ... *developable* + + Used tools: + + * ``dot(1)``: Graphviz (http://www.graphviz.org). If Graphviz is not + available, the DOT language is inserted as literal-block. + + * SVG to PDF: To generate PDF, you need at least one of this tools: + + - ``convert(1)``: ImageMagick (https://www.imagemagick.org) + + List of customizations: + + * generate PDF from SVG / used by PDF (LaTeX) builder + + * generate SVG (html-builder) and PDF (latex-builder) from DOT files. + DOT: see http://www.graphviz.org/content/dot-language + + """ + +import os +from os import path +import subprocess +from hashlib import sha1 +import sys + +from docutils import nodes +from docutils.statemachine import ViewList +from docutils.parsers.rst import directives +from docutils.parsers.rst.directives import images +import sphinx + +from sphinx.util.nodes import clean_astext +from six import iteritems + +PY3 = sys.version_info[0] == 3 + +if PY3: + _unicode = str +else: + _unicode = unicode + +# Get Sphinx version +major, minor, patch = sphinx.version_info[:3] +if major == 1 and minor > 3: + # patches.Figure only landed in Sphinx 1.4 + from sphinx.directives.patches import Figure # pylint: disable=C0413 +else: + Figure = images.Figure + +__version__ = '1.0.0' + +# simple helper +# ------------- + +def which(cmd): + """Searches the ``cmd`` in the ``PATH`` enviroment. + + This *which* searches the PATH for executable ``cmd`` . First match is + returned, if nothing is found, ``None` is returned. + """ + envpath = os.environ.get('PATH', None) or os.defpath + for folder in envpath.split(os.pathsep): + fname = folder + os.sep + cmd + if path.isfile(fname): + return fname + +def mkdir(folder, mode=0o775): + if not path.isdir(folder): + os.makedirs(folder, mode) + +def file2literal(fname): + with open(fname, "r") as src: + data = src.read() + node = nodes.literal_block(data, data) + return node + +def isNewer(path1, path2): + """Returns True if ``path1`` is newer than ``path2`` + + If ``path1`` exists and is newer than ``path2`` the function returns + ``True`` is returned otherwise ``False`` + """ + return (path.exists(path1) + and os.stat(path1).st_ctime > os.stat(path2).st_ctime) + +def pass_handle(self, node): # pylint: disable=W0613 + pass + +# setup conversion tools and sphinx extension +# ------------------------------------------- + +# Graphviz's dot(1) support +dot_cmd = None + +# ImageMagick' convert(1) support +convert_cmd = None + + +def setup(app): + # check toolchain first + app.connect('builder-inited', setupTools) + + # image handling + app.add_directive("kernel-image", KernelImage) + app.add_node(kernel_image, + html = (visit_kernel_image, pass_handle), + latex = (visit_kernel_image, pass_handle), + texinfo = (visit_kernel_image, pass_handle), + text = (visit_kernel_image, pass_handle), + man = (visit_kernel_image, pass_handle), ) + + # figure handling + app.add_directive("kernel-figure", KernelFigure) + app.add_node(kernel_figure, + html = (visit_kernel_figure, pass_handle), + latex = (visit_kernel_figure, pass_handle), + texinfo = (visit_kernel_figure, pass_handle), + text = (visit_kernel_figure, pass_handle), + man = (visit_kernel_figure, pass_handle), ) + + # render handling + app.add_directive('kernel-render', KernelRender) + app.add_node(kernel_render, + html = (visit_kernel_render, pass_handle), + latex = (visit_kernel_render, pass_handle), + texinfo = (visit_kernel_render, pass_handle), + text = (visit_kernel_render, pass_handle), + man = (visit_kernel_render, pass_handle), ) + + app.connect('doctree-read', add_kernel_figure_to_std_domain) + + return dict( + version = __version__, + parallel_read_safe = True, + parallel_write_safe = True + ) + + +def setupTools(app): + u""" + Check available build tools and log some *verbose* messages. + + This function is called once, when the builder is initiated. + """ + global dot_cmd, convert_cmd # pylint: disable=W0603 + app.verbose("kfigure: check installed tools ...") + + dot_cmd = which('dot') + convert_cmd = which('convert') + + if dot_cmd: + app.verbose("use dot(1) from: " + dot_cmd) + else: + app.warn("dot(1) not found, for better output quality install " + "graphviz from http://www.graphviz.org") + if convert_cmd: + app.verbose("use convert(1) from: " + convert_cmd) + else: + app.warn( + "convert(1) not found, for SVG to PDF conversion install " + "ImageMagick (https://www.imagemagick.org)") + + +# integrate conversion tools +# -------------------------- + +RENDER_MARKUP_EXT = { + # The '.ext' must be handled by convert_image(..) function's *in_ext* input. + # : <.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 From a68b5bb6708e73d58b30680421df8166898e5cfc Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Wed, 8 Mar 2017 10:27:55 +0800 Subject: [PATCH 07/37] drm/rockchip: cdn-dp: return error code when clk_get_rate failed The clk_get_rate return 0 if something goes wrong, so it can never be less then zero, the ret should be set a error code, otherwise the cdn_dp_clk_enable will return 0 when it failed at clk_get_rate. In addition, clk_get_rate() returns an "unsigned long", so use "unsigned long" instead of "u32" is better. Signed-off-by: Chris Zhong Signed-off-by: Sean Paul Link: http://patchwork.freedesktop.org/patch/msgid/1488940077-22297-2-git-send-email-zyw@rock-chips.com --- drivers/gpu/drm/rockchip/cdn-dp-core.c | 5 +++-- drivers/gpu/drm/rockchip/cdn-dp-reg.c | 2 +- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index 9ab67a670885f..869976b591f8f 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -94,7 +94,7 @@ static int cdn_dp_grf_write(struct cdn_dp_device *dp, static int cdn_dp_clk_enable(struct cdn_dp_device *dp) { int ret; - u32 rate; + unsigned long rate; ret = clk_prepare_enable(dp->pclk); if (ret < 0) { @@ -123,7 +123,8 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp) rate = clk_get_rate(dp->core_clk); if (!rate) { - DRM_DEV_ERROR(dp->dev, "get clk rate failed: %d\n", rate); + DRM_DEV_ERROR(dp->dev, "get clk rate failed\n"); + ret = -EINVAL; goto err_set_rate; } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index 319dbbaa36099..963d8abec463e 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -29,7 +29,7 @@ #define LINK_TRAINING_RETRY_MS 20 #define LINK_TRAINING_TIMEOUT_MS 500 -void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk) +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk) { writel(clk / 1000000, dp->regs + SW_CLK_H); } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h index b5f2153246942..3507be3e0e122 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h @@ -462,7 +462,7 @@ enum vic_bt_type { void cdn_dp_clock_reset(struct cdn_dp_device *dp); -void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk); +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk); int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, u32 i_size, const u32 *d_mem, u32 d_size); int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable); From 05c00c2f196757cd59d5738ec4d02e365cf9e7a0 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Wed, 8 Mar 2017 10:27:56 +0800 Subject: [PATCH 08/37] drm/rockchip: cdn-dp: Correct PHY register address Correct some DP register address for PHY Configuration according to latest datasheet. Signed-off-by: Chris Zhong Signed-off-by: Sean Paul Link: http://patchwork.freedesktop.org/patch/msgid/1488940077-22297-3-git-send-email-zyw@rock-chips.com --- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h index 3507be3e0e122..c4bbb4a833197 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h @@ -121,12 +121,11 @@ /* dptx phy addr */ #define DP_TX_PHY_CONFIG_REG 0x2000 -#define DP_TX_PHY_STATUS_REG 0x2004 -#define DP_TX_PHY_SW_RESET 0x2008 -#define DP_TX_PHY_SCRAMBLER_SEED 0x200c -#define DP_TX_PHY_TRAINING_01_04 0x2010 -#define DP_TX_PHY_TRAINING_05_08 0x2014 -#define DP_TX_PHY_TRAINING_09_10 0x2018 +#define DP_TX_PHY_SW_RESET 0x2004 +#define DP_TX_PHY_SCRAMBLER_SEED 0x2008 +#define DP_TX_PHY_TRAINING_01_04 0x200c +#define DP_TX_PHY_TRAINING_05_08 0x2010 +#define DP_TX_PHY_TRAINING_09_10 0x2014 #define TEST_COR 0x23fc /* dptx hpd addr */ From 9346ab7d72c0e0526bd9cea70fa4ad768fe27c20 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Wed, 8 Mar 2017 10:27:57 +0800 Subject: [PATCH 09/37] drm/rockchip: cdn-dp: add more log for video config In order to analyze some video config failed, add some useful printouts. Signed-off-by: Chris Zhong Signed-off-by: Sean Paul Link: http://patchwork.freedesktop.org/patch/msgid/1488940077-22297-4-git-send-email-zyw@rock-chips.com --- drivers/gpu/drm/rockchip/cdn-dp-reg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index 963d8abec463e..b14d211f6c217 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -671,6 +671,10 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) rem = do_div(symbol, 1000); if (tu_size_reg > 64) { ret = -EINVAL; + DRM_DEV_ERROR(dp->dev, + "tu error, clk:%d, lanes:%d, rate:%d\n", + mode->clock, dp->link.num_lanes, + link_rate); goto err_config_video; } } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || From 265ffed739d313bd876c8faa9be15d3a101b8175 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sun, 12 Mar 2017 22:46:36 +0800 Subject: [PATCH 10/37] drm/tinydrm: fix semicolon.cocci warnings drivers/gpu/drm/tinydrm/mipi-dbi.c:657:2-3: Unneeded semicolon drivers/gpu/drm/tinydrm/mipi-dbi.c:593:2-3: Unneeded semicolon Remove unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci Signed-off-by: Fengguang Wu Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170312144636.GA91808@lkp-g5.lkp.intel.com --- drivers/gpu/drm/tinydrm/mipi-dbi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c index 2d21b490005cd..856416cec3eb9 100644 --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c @@ -590,7 +590,7 @@ static int mipi_dbi_spi1e_transfer(struct mipi_dbi *mipi, int dc, ret = spi_sync(spi, &m); if (ret) return ret; - }; + } return 0; } @@ -654,7 +654,7 @@ static int mipi_dbi_spi1_transfer(struct mipi_dbi *mipi, int dc, ret = spi_sync(spi, &m); if (ret) return ret; - }; + } return 0; } From 43c27940227039223abbb5107a2eb3d799c8a2ec Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 13 Mar 2017 09:22:26 +0100 Subject: [PATCH 11/37] drm: virtio: fix kmem_cache_alloc error check kmem_cache_alloc returns NULL on error, not ERR_PTR. Fixes: f5985bf9cadd4e3ed8d5d9a9cbbb2e39cdb81cd9 Reported-by: Jiri Slaby Signed-off-by: Gerd Hoffmann Reviewed-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1489393346-13874-1-git-send-email-kraxel@redhat.com --- drivers/gpu/drm/virtio/virtgpu_vq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 472e34986a444..9eb96fb2c1479 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -97,8 +97,8 @@ virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev, struct virtio_gpu_vbuffer *vbuf; vbuf = kmem_cache_alloc(vgdev->vbufs, GFP_KERNEL); - if (IS_ERR(vbuf)) - return ERR_CAST(vbuf); + if (!vbuf) + return ERR_PTR(-ENOMEM); memset(vbuf, 0, VBUFFER_SIZE); BUG_ON(size > MAX_INLINE_CMD_SIZE); From ee5cb7c465360f8eabc351196554e534675bdf6b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 14 Mar 2017 10:54:10 +0300 Subject: [PATCH 12/37] drm: qxl: add missing return check My static checker complains that "release" is uninitialized if qxl_alloc_release_reserved() fails, so let's add a check for that. Signed-off-by: Dan Carpenter Link: http://patchwork.freedesktop.org/patch/msgid/20170314075410.GB5984@mwanda Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/qxl/qxl_display.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 9548bb58d3bcf..058340a002c29 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -572,6 +572,8 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane, ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD, &release, NULL); + if (ret) + return; cmd = (struct qxl_cursor_cmd *) qxl_release_map(qdev, release); From 7d52cb88c9ca20c32288de6ed62b32410c936ccc Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:43 +0100 Subject: [PATCH 13/37] drm: Remove drm_pending_event->pid We might as well dump the drm_file pointer, that's about as useful a cookie as the pid. Noticed while typing docs for drm_file and friends. Since the only consumer of this is the tracepoints I think we can safely change this - those tracepoints should not be uapi relevant at all. It all goes back to commit b9c2c9ae882f058084e13e339925dbf8d2d20271 Author: Jesse Barnes Date: Thu Jul 1 16:48:09 2010 -0700 drm: add per-event vblank event trace points which doesn't give a special justification for using pid over a pointer. Also note that the nouveau code setting it is entirely pointless: Since this isn't a vblank event, it will never hit the vblank tracepoints. Cc: Ben Skeggs Reviewed-by: Sean Paul Reviewed-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-11-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_irq.c | 5 ++--- drivers/gpu/drm/drm_trace.h | 20 ++++++++++---------- drivers/gpu/drm/nouveau/nouveau_usif.c | 1 - include/drm/drm_file.h | 2 -- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 1906723af3893..9bdca69f754cd 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -978,7 +978,7 @@ static void send_vblank_event(struct drm_device *dev, e->event.tv_sec = now->tv_sec; e->event.tv_usec = now->tv_usec; - trace_drm_vblank_event_delivered(e->base.pid, e->pipe, + trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, e->event.sequence); drm_send_event_locked(dev, &e->base); @@ -1505,7 +1505,6 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, } e->pipe = pipe; - e->base.pid = current->pid; e->event.base.type = DRM_EVENT_VBLANK; e->event.base.length = sizeof(e->event); e->event.user_data = vblwait->request.signal; @@ -1534,7 +1533,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n", vblwait->request.sequence, seq, pipe); - trace_drm_vblank_event_queued(current->pid, pipe, + trace_drm_vblank_event_queued(file_priv, pipe, vblwait->request.sequence); e->event.sequence = vblwait->request.sequence; diff --git a/drivers/gpu/drm/drm_trace.h b/drivers/gpu/drm/drm_trace.h index ce3c42813fbbf..14c5a777682e5 100644 --- a/drivers/gpu/drm/drm_trace.h +++ b/drivers/gpu/drm/drm_trace.h @@ -24,36 +24,36 @@ TRACE_EVENT(drm_vblank_event, ); TRACE_EVENT(drm_vblank_event_queued, - TP_PROTO(pid_t pid, int crtc, unsigned int seq), - TP_ARGS(pid, crtc, seq), + TP_PROTO(struct drm_file *file, int crtc, unsigned int seq), + TP_ARGS(file, crtc, seq), TP_STRUCT__entry( - __field(pid_t, pid) + __field(struct drm_file *, file) __field(int, crtc) __field(unsigned int, seq) ), TP_fast_assign( - __entry->pid = pid; + __entry->file = file; __entry->crtc = crtc; __entry->seq = seq; ), - TP_printk("pid=%d, crtc=%d, seq=%u", __entry->pid, __entry->crtc, \ + TP_printk("file=%p, crtc=%d, seq=%u", __entry->file, __entry->crtc, \ __entry->seq) ); TRACE_EVENT(drm_vblank_event_delivered, - TP_PROTO(pid_t pid, int crtc, unsigned int seq), - TP_ARGS(pid, crtc, seq), + TP_PROTO(struct drm_file *file, int crtc, unsigned int seq), + TP_ARGS(file, crtc, seq), TP_STRUCT__entry( - __field(pid_t, pid) + __field(struct drm_file *, file) __field(int, crtc) __field(unsigned int, seq) ), TP_fast_assign( - __entry->pid = pid; + __entry->file = file; __entry->crtc = crtc; __entry->seq = seq; ), - TP_printk("pid=%d, crtc=%d, seq=%u", __entry->pid, __entry->crtc, \ + TP_printk("file=%p, crtc=%d, seq=%u", __entry->file, __entry->crtc, \ __entry->seq) ); diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.c b/drivers/gpu/drm/nouveau/nouveau_usif.c index afbdbed1a6901..9dc10b17ad34f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_usif.c +++ b/drivers/gpu/drm/nouveau/nouveau_usif.c @@ -211,7 +211,6 @@ usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) goto done; ntfy->p->base.event = &ntfy->p->e.base; ntfy->p->base.file_priv = f; - ntfy->p->base.pid = current->pid; ntfy->p->e.base.type = DRM_NOUVEAU_EVENT_NVIF; ntfy->p->e.base.length = sizeof(ntfy->p->e.base) + ntfy->reply; diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index d1a25cc17fd16..4e347399a7bda 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -75,8 +75,6 @@ struct drm_pending_event { struct list_head 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 */ From b93658f83fa7f89cde5cfba7158da2be8cac42b2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:44 +0100 Subject: [PATCH 14/37] drm/doc: Document drm_file.[hc] Well, mostly drm_file.h, and clean up all related things: - I didnt' figure out the difference between preclose and postclose. The existing explanation in drm-internals.rst didn't convince me, since it's also really outdated - we clean up pending DRM events in the core nowadays. I put a FIXME in for the future. - Another FIXME is to have a macro for default fops. - Lots of links all around, main areas are to tie the overview in drm_file.c more into the callbacks in struct drm_device, and the other is to link render/primary node code to the right sections in drm-uapi.rst. - Also moved the open/close stuff to drm_drv.h from drm-internals.rst, seems like the better place for that information. Since that section was rather outdated this amounted to full-on rewrite. A big missing piece here is some overview graph, but I think better to wait with that one until drm_device and drm_driver are also fully documented. v2: Nits from Sean. Reviewed-by: Sean Paul Reviewed-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-12-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-internals.rst | 52 +----- Documentation/gpu/drm-uapi.rst | 4 + drivers/gpu/drm/drm_file.c | 66 ++++--- include/drm/drm_drv.h | 81 ++++++++- include/drm/drm_file.h | 265 ++++++++++++++++++++++++---- 5 files changed, 354 insertions(+), 114 deletions(-) diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index 3bb4d937cdfee..cbf8e52cd9bbc 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -243,61 +243,15 @@ drivers. Open/Close, File Operations and IOCTLs ====================================== -Open and Close --------------- - -Open and close handlers. None of those methods are mandatory:: - - int (*firstopen) (struct drm_device *); - void (*lastclose) (struct drm_device *); - int (*open) (struct drm_device *, struct drm_file *); - void (*preclose) (struct drm_device *, struct drm_file *); - void (*postclose) (struct drm_device *, struct drm_file *); - -The firstopen method is called by the DRM core for legacy UMS (User Mode -Setting) drivers only when an application opens a device that has no -other opened file handle. UMS drivers can implement it to acquire device -resources. KMS drivers can't use the method and must acquire resources -in the load method instead. - -Similarly the lastclose method is called when the last application -holding a file handle opened on the device closes it, for both UMS and -KMS drivers. Additionally, the method is also called at module unload -time or, for hot-pluggable devices, when the device is unplugged. The -firstopen and lastclose calls can thus be unbalanced. - -The open method is called every time the device is opened by an -application. Drivers can allocate per-file private data in this method -and store them in the struct :c:type:`struct drm_file -` driver_priv field. Note that the open method is -called before firstopen. - -The close operation is split into preclose and postclose methods. -Drivers must stop and cleanup all per-file operations in the preclose -method. For instance pending vertical blanking and page flip events must -be cancelled. No per-file operation is allowed on the file handle after -returning from the preclose method. - -Finally the postclose method is called as the last step of the close -operation, right before calling the lastclose method if no other open -file handle exists for the device. Drivers that have allocated per-file -private data in the open method should free it here. - -The lastclose method should restore CRTC and plane properties to default -value, so that a subsequent open of the device will not inherit state -from the previous user. It can also be used to execute delayed power -switching state changes, e.g. in conjunction with the :ref:`vga_switcheroo` -infrastructure. Beyond that KMS drivers should not do any -further cleanup. Only legacy UMS drivers might need to clean up device -state so that the vga console or an independent fbdev driver could take -over. - File Operations --------------- .. kernel-doc:: drivers/gpu/drm/drm_file.c :doc: file operations +.. kernel-doc:: include/drm/drm_file.h + :internal: + .. kernel-doc:: drivers/gpu/drm/drm_file.c :export: diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index fcc228ef5bc49..352652810dab7 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -21,6 +21,8 @@ libdrm Device Lookup :doc: getunique and setversion story +.. _drm_primary_node: + Primary Nodes, DRM Master and Authentication ============================================ @@ -103,6 +105,8 @@ is already rather painful for the DRM subsystem, with multiple different uAPIs for the same thing co-existing. If we add a few more complete mistakes into the mix every year it would be entirely unmanageable. +.. _drm_render_node: + Render nodes ============ diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index d9e63d73d3ecb..92848fe75ea66 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -50,12 +50,14 @@ DEFINE_MUTEX(drm_global_mutex); * * Drivers must define the file operations structure that forms the DRM * userspace API entry point, even though most of those operations are - * implemented in the DRM core. The mandatory functions are drm_open(), + * implemented in the DRM core. The resulting &struct file_operations must be + * stored in the &drm_driver.fops field. The mandatory functions are drm_open(), * drm_read(), drm_ioctl() and drm_compat_ioctl() if CONFIG_COMPAT is enabled - * (note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n). Drivers which - * implement private ioctls that require 32/64 bit compatibility support must - * provide their own .compat_ioctl() handler that processes private ioctls and - * calls drm_compat_ioctl() for core ioctls. + * Note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n, so there's no + * need to sprinkle #ifdef into the code. Drivers which implement private ioctls + * that require 32/64 bit compatibility support must provide their own + * &file_operations.compat_ioctl handler that processes private ioctls and calls + * drm_compat_ioctl() for core ioctls. * * In addition drm_read() and drm_poll() provide support for DRM events. DRM * events are a generic and extensible means to send asynchronous events to @@ -63,10 +65,14 @@ DEFINE_MUTEX(drm_global_mutex); * page flip completions by the KMS API. But drivers can also use it for their * own needs, e.g. to signal completion of rendering. * + * For the driver-side event interface see drm_event_reserve_init() and + * drm_send_event() as the main starting points. + * * The memory mapping implementation will vary depending on how the driver * manages memory. Legacy drivers will use the deprecated drm_legacy_mmap() * function, modern drivers should use one of the provided memory-manager - * specific implementations. For GEM-based drivers this is drm_gem_mmap(). + * specific implementations. For GEM-based drivers this is drm_gem_mmap(), and + * for drivers which use the CMA GEM helpers it's drm_gem_cma_mmap(). * * No other file operations are supported by the DRM userspace API. Overall the * following is an example #file_operations structure:: @@ -82,6 +88,9 @@ DEFINE_MUTEX(drm_global_mutex); * .llseek = no_llseek, * .mmap = drm_gem_mmap, * }; + * + * FIXME: We should have a macro for this (and the CMA version) so that drivers + * don't have to repeat it all the time. */ static int drm_open_helper(struct file *filp, struct drm_minor *minor); @@ -111,9 +120,9 @@ static int drm_setup(struct drm_device * dev) * @inode: device inode * @filp: file pointer. * - * This function must be used by drivers as their .open() #file_operations - * method. It looks up the correct DRM device and instantiates all the per-file - * resources for it. + * This function must be used by drivers as their &file_operations.open method. + * It looks up the correct DRM device and instantiates all the per-file + * resources for it. It also calls the &drm_driver.open driver callback. * * RETURNS: * @@ -298,11 +307,6 @@ static void drm_events_release(struct drm_file *file_priv) spin_unlock_irqrestore(&dev->event_lock, flags); } -/* - * drm_legacy_dev_reinit - * - * Reinitializes a legacy/ums drm device in it's lastclose function. - */ static void drm_legacy_dev_reinit(struct drm_device *dev) { if (dev->irq_enabled) @@ -327,15 +331,6 @@ static void drm_legacy_dev_reinit(struct drm_device *dev) DRM_DEBUG("lastclose completed\n"); } -/* - * Take down the DRM device. - * - * \param dev DRM device structure. - * - * Frees every resource in \p dev. - * - * \sa drm_device - */ void drm_lastclose(struct drm_device * dev) { DRM_DEBUG("\n"); @@ -353,9 +348,11 @@ void drm_lastclose(struct drm_device * dev) * @inode: device inode * @filp: file pointer. * - * This function must be used by drivers as their .release() #file_operations - * method. It frees any resources associated with the open file, and if this is - * the last open file for the DRM device also proceeds to call drm_lastclose(). + * This function must be used by drivers as their &file_operations.release + * method. It frees any resources associated with the open file, and calls the + * &drm_driver.preclose and &drm_driver.lastclose driver callbacks. If this is + * the last open file for the DRM device also proceeds to call the + * &drm_driver.lastclose driver callback. * * RETURNS: * @@ -443,13 +440,13 @@ EXPORT_SYMBOL(drm_release); * @count: count in bytes to read * @offset: offset to read * - * This function must be used by drivers as their .read() #file_operations + * This function must be used by drivers as their &file_operations.read * method iff they use DRM events for asynchronous signalling to userspace. * Since events are used by the KMS API for vblank and page flip completion this * means all modern display drivers must use it. * - * @offset is ignore, DRM events are read like a pipe. Therefore drivers also - * must set the .llseek() #file_operation to no_llseek(). Polling support is + * @offset is ignored, DRM events are read like a pipe. Therefore drivers also + * must set the &file_operation.llseek to no_llseek(). Polling support is * provided by drm_poll(). * * This function will only ever read a full event. Therefore userspace must @@ -537,10 +534,10 @@ EXPORT_SYMBOL(drm_read); * @filp: file pointer * @wait: poll waiter table * - * This function must be used by drivers as their .read() #file_operations - * method iff they use DRM events for asynchronous signalling to userspace. - * Since events are used by the KMS API for vblank and page flip completion this - * means all modern display drivers must use it. + * This function must be used by drivers as their &file_operations.read method + * iff they use DRM events for asynchronous signalling to userspace. Since + * events are used by the KMS API for vblank and page flip completion this means + * all modern display drivers must use it. * * See also drm_read(). * @@ -650,7 +647,8 @@ EXPORT_SYMBOL(drm_event_reserve_init); * @p: tracking structure for the pending event * * This function frees the event @p initialized with drm_event_reserve_init() - * and releases any allocated space. + * and releases any allocated space. It is used to cancel an event when the + * nonblocking operation could not be submitted and needed to be aborted. */ void drm_event_cancel_free(struct drm_device *dev, struct drm_pending_event *p) diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 4321d012c4bab..8f900fb302755 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -64,7 +64,6 @@ struct drm_mode_create_dumb; * structure for GEM drivers. */ struct drm_driver { - /** * @load: * @@ -76,14 +75,94 @@ struct drm_driver { * See drm_dev_init() and drm_dev_register() for proper and * race-free way to set up a &struct drm_device. * + * This is deprecated, do not use! + * * Returns: * * Zero on success, non-zero value on failure. */ int (*load) (struct drm_device *, unsigned long flags); + + /** + * @open: + * + * Driver callback when a new &struct drm_file is opened. Useful for + * setting up driver-private data structures like buffer allocators, + * execution contexts or similar things. Such driver-private resources + * must be released again in @postclose. + * + * Since the display/modeset side of DRM can only be owned by exactly + * one &struct drm_file (see &drm_file.is_master and &drm_device.master) + * there should never be a need to set up any modeset related resources + * in this callback. Doing so would be a driver design bug. + * + * Returns: + * + * 0 on success, a negative error code on failure, which will be + * promoted to userspace as the result of the open() system call. + */ int (*open) (struct drm_device *, struct drm_file *); + + /** + * @preclose: + * + * One of the driver callbacks when a new &struct drm_file is closed. + * Useful for tearing down driver-private data structures allocated in + * @open like buffer allocators, execution contexts or similar things. + * + * Since the display/modeset side of DRM can only be owned by exactly + * one &struct drm_file (see &drm_file.is_master and &drm_device.master) + * there should never be a need to tear down any modeset related + * resources in this callback. Doing so would be a driver design bug. + * + * FIXME: It is not really clear why there's both @preclose and + * @postclose. Without a really good reason, use @postclose only. + */ void (*preclose) (struct drm_device *, struct drm_file *file_priv); + + /** + * @postclose: + * + * One of the driver callbacks when a new &struct drm_file is closed. + * Useful for tearing down driver-private data structures allocated in + * @open like buffer allocators, execution contexts or similar things. + * + * Since the display/modeset side of DRM can only be owned by exactly + * one &struct drm_file (see &drm_file.is_master and &drm_device.master) + * there should never be a need to tear down any modeset related + * resources in this callback. Doing so would be a driver design bug. + * + * FIXME: It is not really clear why there's both @preclose and + * @postclose. Without a really good reason, use @postclose only. + */ void (*postclose) (struct drm_device *, struct drm_file *); + + /** + * @lastclose: + * + * Called when the last &struct drm_file has been closed and there's + * currently no userspace client for the &struct drm_device. + * + * Modern drivers should only use this to force-restore the fbdev + * framebuffer using drm_fb_helper_restore_fbdev_mode_unlocked(). + * Anything else would indicate there's something seriously wrong. + * Modern drivers can also use this to execute delayed power switching + * state changes, e.g. in conjunction with the :ref:`vga_switcheroo` + * infrastructure. + * + * This is called after @preclose and @postclose have been called. + * + * NOTE: + * + * All legacy drivers use this callback to de-initialize the hardware. + * This is purely because of the shadow-attach model, where the DRM + * kernel driver does not really own the hardware. Instead ownershipe is + * handled with the help of userspace through an inheritedly racy dance + * to set/unset the VT into raw mode. + * + * Legacy drivers initialize the hardware in the @firstopen callback, + * which isn't even called for modern drivers. + */ void (*lastclose) (struct drm_device *); /** diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 4e347399a7bda..5dd27ae5c47c0 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -45,6 +45,7 @@ struct drm_device; * FIXME: Not sure we want to have drm_minor here in the end, but to avoid * header include loops we need it here for now. */ + enum drm_minor_type { DRM_MINOR_PRIMARY, DRM_MINOR_CONTROL, @@ -52,12 +53,19 @@ enum drm_minor_type { }; /** - * DRM minor structure. This structure represents a drm minor number. + * struct drm_minor - DRM device minor structure + * + * This structure represents a DRM minor number for device nodes in /dev. + * Entirely opaque to drivers and should never be inspected directly by drivers. + * Drivers instead should only interact with &struct drm_file and of course + * &struct drm_device, which is also where driver-private data and resources can + * be attached to. */ struct drm_minor { - int index; /**< Minor device number */ - int type; /**< Control or render */ - struct device *kdev; /**< Linux device */ + /* private: */ + int index; /* Minor device number */ + int type; /* Control or render */ + struct device *kdev; /* Linux device */ struct drm_device *dev; struct dentry *debugfs_root; @@ -66,89 +74,286 @@ struct drm_minor { struct mutex debugfs_lock; /* Protects debugfs_list. */ }; -/* Event queued up for userspace to read */ +/** + * struct drm_pending_event - Event queued up for userspace to read + * + * This represents a DRM event. Drivers can use this as a generic completion + * mechanism, which supports kernel-internal &struct completion, &struct dma_fence + * and also the DRM-specific &struct drm_event delivery mechanism. + */ struct drm_pending_event { + /** + * @completion: + * + * Optional pointer to a kernel internal completion signalled when + * drm_send_event() is called, useful to internally synchronize with + * nonblocking operations. + */ struct completion *completion; + + /** + * @completion_release: + * + * Optional callback currently only used by the atomic modeset helpers + * to clean up the reference count for the structure @completion is + * stored in. + */ void (*completion_release)(struct completion *completion); + + /** + * @event: + * + * Pointer to the actual event that should be sent to userspace to be + * read using drm_read(). Can be optional, since nowadays events are + * also used to signal kernel internal threads with @completion or DMA + * transactions using @fence. + */ struct drm_event *event; + + /** + * @fence: + * + * Optional DMA fence to unblock other hardware transactions which + * depend upon the nonblocking DRM operation this event represents. + */ struct dma_fence *fence; + + /** + * @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; + + /** + * @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 drm_file *file_priv; }; -/** File private data */ +/** + * struct drm_file - DRM file private data + * + * This structure tracks DRM state per open file descriptor. + */ struct drm_file { + /** + * @authenticated: + * + * Whether the client is allowed to submit rendering, which for legacy + * nodes means it must be authenticated. + * + * See also the :ref:`section on primary nodes and authentication + * `. + */ unsigned authenticated :1; - /* true when the client has asked us to expose stereo 3D mode flags */ + + /** + * @stereo_allowed: + * + * True when the client has asked us to expose stereo 3D mode flags. + */ unsigned stereo_allowed :1; - /* - * true if client understands CRTC primary planes and cursor planes - * in the plane list + + /** + * @universal_planes: + * + * True if client understands CRTC primary planes and cursor planes + * in the plane list. Automatically set when @atomic is set. */ unsigned universal_planes:1; - /* true if client understands atomic properties */ + + /** @atomic: True if client understands atomic properties. */ unsigned atomic:1; - /* - * This client is the creator of @master. - * Protected by struct drm_device::master_mutex. + + /** + * @is_master: + * + * This client is the creator of @master. Protected by struct + * &drm_device.master_mutex. + * + * See also the :ref:`section on primary nodes and authentication + * `. */ 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 `. + */ + struct drm_master *master; + + /** @pid: Process that opened this file. */ struct pid *pid; + + /** @magic: Authentication magic, see @authenticated. */ drm_magic_t magic; + + /** + * @lhead: + * + * List of all open files of a DRM device, linked into + * &drm_device.filelist. Protected by &drm_device.filelist_mutex. + */ struct list_head lhead; + + /** @minor: &struct drm_minor for this file. */ struct drm_minor *minor; - 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; - /** Lock for synchronization of access to object_idr. */ + + /** @table_lock: Protects @object_idr. */ spinlock_t table_lock; + /** @filp: Pointer to the core file structure. */ struct file *filp; + + /** + * @driver_priv: + * + * Optional pointer for driver private data. Can be allocated in + * &drm_driver.open and should be freed in &drm_driver.postclose. + */ void *driver_priv; - struct drm_master *master; /* master this node is currently associated with - N.B. not always dev->master */ /** - * fbs - List of framebuffers associated with this file. + * @fbs: + * + * List of &struct drm_framebuffer associated with this file, using the + * &drm_framebuffer.filp_head entry. * - * Protected by fbs_lock. Note that the fbs list holds a reference on - * the fb object to prevent it from untimely disappearing. + * Protected by @fbs_lock. Note that the @fbs list holds a reference on + * the framebuffer object to prevent it from untimely disappearing. */ struct list_head fbs; + + /** @fbs_lock: Protects @fbs. */ struct mutex fbs_lock; - /** User-created blob properties; this retains a reference on the - * property. */ + /** + * @blobs: + * + * User-created blob properties; this retains a reference on the + * property. + * + * Protected by @drm_mode_config.blob_lock; + */ struct list_head blobs; + /** @event_wait: Waitqueue for new events added to @event_list. */ wait_queue_head_t event_wait; + + /** + * @pending_event_list: + * + * List of pending &struct drm_pending_event, used to clean up pending + * events in case this file gets closed before the event is signalled. + * Uses the &drm_pending_event.pending_link entry. + * + * Protect by &drm_device.event_lock. + */ struct list_head pending_event_list; + + /** + * @event_list: + * + * List of &struct drm_pending_event, ready for delivery to userspace + * through drm_read(). Uses the &drm_pending_event.link entry. + * + * Protect by &drm_device.event_lock. + */ struct list_head event_list; + + /** + * @event_space: + * + * Available event space to prevent userspace from + * exhausting kernel memory. Currently limited to the fairly arbitrary + * value of 4KB. + */ int event_space; + /** @event_read_lock: Serializes drm_read(). */ struct mutex event_read_lock; + /** + * @prime: + * + * Per-file buffer caches used by the PRIME buffer sharing code. + */ struct drm_prime_file_private prime; + + /* private: */ + unsigned long lock_count; /* DRI1 legacy lock count */ }; +/** + * drm_is_primary_client - is this an open file of the primary node + * @file_priv: DRM file + * + * Returns true if this is an open file of the primary node, i.e. + * &drm_file.minor of @file_priv is a primary minor. + * + * See also the :ref:`section on primary nodes and authentication + * `. + */ +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 `. + */ static inline bool drm_is_render_client(const struct drm_file *file_priv) { return file_priv->minor->type == DRM_MINOR_RENDER; } +/** + * drm_is_control_client - is this an open file of the control node + * @file_priv: DRM file + * + * Control nodes are deprecated and in the process of getting removed from the + * DRM userspace API. Do not ever use! + */ static inline bool drm_is_control_client(const struct drm_file *file_priv) { return file_priv->minor->type == DRM_MINOR_CONTROL; } -static inline bool drm_is_primary_client(const struct drm_file *file_priv) -{ - return file_priv->minor->type == DRM_MINOR_PRIMARY; -} - int drm_open(struct inode *inode, struct file *filp); ssize_t drm_read(struct file *filp, char __user *buffer, size_t count, loff_t *offset); From 098899fee681b96905da97bbcce1a548e8f9e645 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:50 +0100 Subject: [PATCH 15/37] drm/vgem: switch to postclose I didn't spot anything that would require ordering here (well not anywhere else either), and I'm trying to unify at least modern drivers on one close hook. Cc: Chris Wilson Reviewed-by: Sean Paul Reviewed-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-18-daniel.vetter@ffwll.ch --- drivers/gpu/drm/vgem/vgem_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index 477e07f0ecb67..461712e2f0d12 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -103,7 +103,7 @@ static int vgem_open(struct drm_device *dev, struct drm_file *file) return 0; } -static void vgem_preclose(struct drm_device *dev, struct drm_file *file) +static void vgem_postclose(struct drm_device *dev, struct drm_file *file) { struct vgem_file *vfile = file->driver_priv; @@ -302,7 +302,7 @@ static int vgem_prime_mmap(struct drm_gem_object *obj, static struct drm_driver vgem_driver = { .driver_features = DRIVER_GEM | DRIVER_PRIME, .open = vgem_open, - .preclose = vgem_preclose, + .postclose = vgem_postclose, .gem_free_object_unlocked = vgem_gem_free_object, .gem_vm_ops = &vgem_gem_vm_ops, .ioctls = vgem_ioctls, From d55f7e5d54c31c8ed61fdbfc59835cd6804ba954 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:56 +0100 Subject: [PATCH 16/37] drm: Create DEFINE_DRM_GEM_CMA_FOPS and roll it out to drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Less code ftw. This converts all drivers except the tinydrm helper module. That one needs more work, since it gets the THIS_MODULE reference from tinydrm.ko instead of the actual driver module like it should. Probably needs a similar trick like I used here with generating the entire struct with a macro. Cc: Noralf Trønnes Reviewed-by: Sean Paul Reviewed-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-24-daniel.vetter@ffwll.ch --- drivers/gpu/drm/arm/hdlcd_drv.c | 12 +-------- drivers/gpu/drm/arm/malidp_drv.c | 12 +-------- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 12 +-------- drivers/gpu/drm/drm_file.c | 3 +++ drivers/gpu/drm/drm_gem_cma_helper.c | 3 +++ drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c | 12 +-------- .../gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 12 +-------- drivers/gpu/drm/imx/imx-drm-core.c | 11 +------- drivers/gpu/drm/meson/meson_drv.c | 14 +--------- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 14 +--------- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 12 +-------- drivers/gpu/drm/shmobile/shmob_drm_drv.c | 12 +-------- drivers/gpu/drm/sti/sti_drv.c | 11 +------- drivers/gpu/drm/sun4i/sun4i_drv.c | 12 +-------- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 12 +-------- drivers/gpu/drm/zte/zx_drm_drv.c | 14 +--------- include/drm/drm_gem_cma_helper.h | 26 +++++++++++++++++++ 17 files changed, 46 insertions(+), 158 deletions(-) diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 4840dc2773390..0e74880b5e94e 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -239,17 +239,7 @@ static int hdlcd_debugfs_init(struct drm_minor *minor) } #endif -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver hdlcd_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index a9608a2e5a298..ea2546f766c2f 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -178,17 +178,7 @@ static void malidp_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(malidp->fbdev); } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver malidp_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 970bd87d7ccec..178acbf5f6c53 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -724,17 +724,7 @@ static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver atmel_hlcdc_dc_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 92848fe75ea66..b42c1334112c4 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -89,6 +89,9 @@ DEFINE_MUTEX(drm_global_mutex); * .mmap = drm_gem_mmap, * }; * + * For CMA based drivers there is the DEFINE_DRM_GEM_CMA_FOPS() macro to make + * this simpler. + * * FIXME: We should have a macro for this (and the CMA version) so that drivers * don't have to repeat it all the time. */ diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 906984d4bec23..f478a597cd792 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -338,6 +338,9 @@ static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj, * as their ->mmap() handler in the DRM device file's file_operations * structure. * + * Instead of directly referencing this function, drivers should use the + * DEFINE_DRM_GEM_CMA_FOPS().macro. + * * Returns: * 0 on success or a negative error code on failure. */ diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index b5391c124c647..6e00f4b267f17 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -161,17 +161,7 @@ static void fsl_dcu_drm_lastclose(struct drm_device *dev) drm_fbdev_cma_restore_mode(fsl_dev->fbdev); } -static const struct file_operations fsl_dcu_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fsl_dcu_drm_fops); static struct drm_driver fsl_dcu_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 7ec93aec7e880..df4f50713e54a 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -146,17 +146,7 @@ static int kirin_drm_kms_init(struct drm_device *dev) return ret; } -static const struct file_operations kirin_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(kirin_drm_fops); static int kirin_gem_cma_dumb_create(struct drm_file *file, struct drm_device *dev, diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 4b7b92a7bcf7b..70736615983c1 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -57,16 +57,7 @@ static void imx_drm_driver_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(imxdrm->fbhelper); } -static const struct file_operations imx_drm_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .mmap = drm_gem_cma_mmap, - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, -}; +DEFINE_DRM_GEM_CMA_FOPS(imx_drm_driver_fops); void imx_drm_connector_destroy(struct drm_connector *connector) { diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 8d17d0e59cbe1..bc562a07847b3 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -91,19 +91,7 @@ static irqreturn_t meson_irq(int irq, void *arg) return IRQ_HANDLED; } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, -#endif - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver meson_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index a4633ada8429d..5ac712325c758 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -319,19 +319,7 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, -#endif - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver mxsfb_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 192346d4fb34a..62a3b3e321539 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -226,17 +226,7 @@ static void rcar_du_lastclose(struct drm_device *dev) drm_fbdev_cma_restore_mode(rcdu->fbdev); } -static const struct file_operations rcar_du_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops); static struct drm_driver rcar_du_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 1c7b318b89980..800d1d2c435d0 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -127,17 +127,7 @@ static irqreturn_t shmob_drm_irq(int irq, void *arg) return IRQ_HANDLED; } -static const struct file_operations shmob_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(shmob_drm_fops); static struct drm_driver shmob_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 6003c664ba0b2..a4b5742832690 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -167,16 +167,7 @@ static void sti_mode_config_init(struct drm_device *dev) dev->mode_config.funcs = &sti_mode_config_funcs; } -static const struct file_operations sti_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .mmap = drm_gem_cma_mmap, - .poll = drm_poll, - .read = drm_read, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .release = drm_release, -}; +DEFINE_DRM_GEM_CMA_FOPS(sti_driver_fops); static struct drm_driver sti_driver = { .driver_features = DRIVER_MODESET | diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 9ccf7c4deb6d3..329ea56106a5c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -25,17 +25,7 @@ #include "sun4i_framebuffer.h" #include "sun4i_layer.h" -static const struct file_operations sun4i_drv_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops); static struct drm_driver sun4i_drv_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 5b4ed0ea77683..d7ae5be56d12c 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -529,17 +529,7 @@ static int tilcdc_debugfs_init(struct drm_minor *minor) } #endif -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver tilcdc_driver = { .driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c index b24a70ba4b839..614e854f6be59 100644 --- a/drivers/gpu/drm/zte/zx_drm_drv.c +++ b/drivers/gpu/drm/zte/zx_drm_drv.c @@ -53,19 +53,7 @@ static void zx_drm_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(priv->fbdev); } -static const struct file_operations zx_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, -#endif - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(zx_drm_fops); static struct drm_driver zx_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h index 2abcd5190cc13..f962d33667cff 100644 --- a/include/drm/drm_gem_cma_helper.h +++ b/include/drm/drm_gem_cma_helper.h @@ -26,6 +26,32 @@ to_drm_gem_cma_obj(struct drm_gem_object *gem_obj) return container_of(gem_obj, struct drm_gem_cma_object, base); } +/** + * DEFINE_DRM_GEM_CMA_FOPS() - macro to generate file operations for CMA drivers + * @name: name for the generated structure + * + * This macro autogenerates a suitable &struct file_operations for CMA based + * drivers, which can be assigned to &drm_driver.fops. Note that this structure + * cannot be shared between drivers, because it contains a reference to the + * current module using THIS_MODULE. + * + * Note that the declaration is already marked as static - if you need a + * non-static version of this you're probably doing it wrong and will break the + * THIS_MODULE reference by accident. + */ +#define DEFINE_DRM_GEM_CMA_FOPS(name) \ + static const struct file_operations name = {\ + .owner = THIS_MODULE,\ + .open = drm_open,\ + .release = drm_release,\ + .unlocked_ioctl = drm_ioctl,\ + .compat_ioctl = drm_compat_ioctl,\ + .poll = drm_poll,\ + .read = drm_read,\ + .llseek = noop_llseek,\ + .mmap = drm_gem_cma_mmap,\ + } + /* free GEM object */ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj); From f42e181935d5e5670c87d31ae48063a495bbacae Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:57 +0100 Subject: [PATCH 17/37] drm/gem: Add DEFINE_DRM_GEM_FOPS Sadly there's only 1 driver which can use it, everyone else is special for some reason: - gma500 has a horrible runtime PM ioctl wrapper that probably doesn't really work but meh. - i915 needs special compat_ioctl handler because regrets. - arcgpu needs to fixup the pgprot because (no idea why it can't do that in the fault handler like everyone else). - tegra does even worse stuff with pgprot - udl does something with vm_flags too ... - cma helpers, etnaviv, mtk, msm, rockchip, omap all implement some variation on prefaulting. - exynos is exynos, I got lost in the midlayers. - vc4 has to reinvent half of cma helpers because those are too much midlayer, plus vm_flags dances. - vgem also seems unhappy with the default vm_flags. So pretty sad divergence and I'm sure we could do better, but not really an idea. Oh well, maybe this macro here helps to encourage more consistency at least going forward. Reviewed-by: Sean Paul Reviewed-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-25-daniel.vetter@ffwll.ch --- drivers/gpu/drm/armada/armada_drv.c | 11 +---------- drivers/gpu/drm/drm_file.c | 8 +++----- include/drm/drm_gem.h | 26 ++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 408e00b64cf61..e618fab7f519d 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -60,16 +60,7 @@ static void armada_drm_lastclose(struct drm_device *dev) armada_fbdev_lastclose(dev); } -static const struct file_operations armada_drm_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = drm_read, - .poll = drm_poll, - .unlocked_ioctl = drm_ioctl, - .mmap = drm_gem_mmap, - .open = drm_open, - .release = drm_release, -}; +DEFINE_DRM_GEM_FOPS(armada_drm_fops); static struct drm_driver armada_drm_driver = { .lastclose = armada_drm_lastclose, diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index b42c1334112c4..3783b659cd38a 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -89,11 +89,9 @@ DEFINE_MUTEX(drm_global_mutex); * .mmap = drm_gem_mmap, * }; * - * For CMA based drivers there is the DEFINE_DRM_GEM_CMA_FOPS() macro to make - * this simpler. - * - * FIXME: We should have a macro for this (and the CMA version) so that drivers - * don't have to repeat it all the time. + * 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); diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index b9ade75ecd82e..663d803580578 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -178,6 +178,32 @@ struct drm_gem_object { struct dma_buf_attachment *import_attach; }; +/** + * DEFINE_DRM_GEM_FOPS() - macro to generate file operations for GEM drivers + * @name: name for the generated structure + * + * This macro autogenerates a suitable &struct file_operations for GEM based + * drivers, which can be assigned to &drm_driver.fops. Note that this structure + * cannot be shared between drivers, because it contains a reference to the + * current module using THIS_MODULE. + * + * Note that the declaration is already marked as static - if you need a + * non-static version of this you're probably doing it wrong and will break the + * THIS_MODULE reference by accident. + */ +#define DEFINE_DRM_GEM_FOPS(name) \ + static const struct file_operations name = {\ + .owner = THIS_MODULE,\ + .open = drm_open,\ + .release = drm_release,\ + .unlocked_ioctl = drm_ioctl,\ + .compat_ioctl = drm_compat_ioctl,\ + .poll = drm_poll,\ + .read = drm_read,\ + .llseek = noop_llseek,\ + .mmap = drm_gem_mmap,\ + } + void drm_gem_object_release(struct drm_gem_object *obj); void drm_gem_object_free(struct kref *kref); int drm_gem_object_init(struct drm_device *dev, From 2564d0b043cbe9d762f97104029281102cfe4be2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 2 Mar 2017 16:16:35 +0100 Subject: [PATCH 18/37] drm/doc: Add KMS overview graphs Oh, the shiny and pretties! v2: Review from Laurent. Cc: Laurent Pinchart Acked-by: Eric Anholt Reviewed-by: Gabriel Krisman Bertazi Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170302151638.1882-3-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-kms-helpers.rst | 4 + Documentation/gpu/drm-kms.rst | 134 +++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 03040aa14fe87..012b6ff3ec3fb 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -114,6 +114,8 @@ Framebuffer CMA Helper Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_fb_cma_helper.c :export: +.. _drm_bridges: + Bridges ======= @@ -139,6 +141,8 @@ Bridge Helper Reference .. kernel-doc:: drivers/gpu/drm/drm_bridge.c :export: +.. _drm_panel_helper: + Panel Helper Reference ====================== diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 4d4068855ec41..bcf475f86e830 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -15,7 +15,139 @@ be setup by initializing the following fields. - struct drm_mode_config_funcs \*funcs; Mode setting functions. -Mode Configuration +Overview +======== + +.. kernel-render:: DOT + :alt: KMS Display Pipeline + :caption: KMS Display Pipeline Overview + + digraph "KMS" { + node [shape=box] + + subgraph cluster_static { + style=dashed + label="Static Objects" + + node [bgcolor=grey style=filled] + "drm_plane A" -> "drm_crtc" + "drm_plane B" -> "drm_crtc" + "drm_crtc" -> "drm_encoder A" + "drm_crtc" -> "drm_encoder B" + } + + subgraph cluster_user_created { + style=dashed + label="Userspace-Created" + + node [shape=oval] + "drm_framebuffer 1" -> "drm_plane A" + "drm_framebuffer 2" -> "drm_plane B" + } + + subgraph cluster_connector { + style=dashed + label="Hotpluggable" + + "drm_encoder A" -> "drm_connector A" + "drm_encoder B" -> "drm_connector B" + } + } + +The basic object structure KMS presents to userspace is fairly simple. +Framebuffers (represented by :c:type:`struct drm_framebuffer `, +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 `, 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 `, 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 `, 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 +`) 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 +`, 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 `. + +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 ================================= From 1ea3576874790ba81ab00c3a0f1a3fc23e687062 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 2 Mar 2017 16:16:36 +0100 Subject: [PATCH 19/37] drm/doc: Consistent kerneldoc include order First overview text (if there is any), then headers (since generally you want to start out with the data structures), then all the other stuff with functions. Most of this is pre-shpinx, since with the old docbook only the overview stuff was pulled in directly. Everything else was put in a per-section index, so include order didn't really matter. Acked-by: Eric Anholt Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170302151638.1882-4-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-internals.rst | 6 +++--- Documentation/gpu/drm-kms-helpers.rst | 22 +++++++++++----------- Documentation/gpu/drm-kms.rst | 24 ++++++++++++------------ Documentation/gpu/drm-mm.rst | 24 ++++++++++++------------ 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index cbf8e52cd9bbc..a09c721f9e89f 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -140,12 +140,12 @@ Device Instance and Driver Handling .. kernel-doc:: drivers/gpu/drm/drm_drv.c :doc: driver instance overview -.. kernel-doc:: drivers/gpu/drm/drm_drv.c - :export: - .. kernel-doc:: include/drm/drm_drv.h :internal: +.. kernel-doc:: drivers/gpu/drm/drm_drv.c + :export: + Driver Load ----------- diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 012b6ff3ec3fb..050ebe81d256b 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -37,10 +37,10 @@ Modeset Helper Reference for Common Vtables =========================================== .. kernel-doc:: include/drm/drm_modeset_helper_vtables.h - :internal: + :doc: overview .. kernel-doc:: include/drm/drm_modeset_helper_vtables.h - :doc: overview + :internal: Atomic Modeset Helper Functions Reference ========================================= @@ -84,27 +84,27 @@ Legacy CRTC/Modeset Helper Functions Reference Simple KMS Helper Reference =========================== +.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c + :doc: overview + .. kernel-doc:: include/drm/drm_simple_kms_helper.h :internal: .. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c :export: -.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c - :doc: overview - fbdev Helper Functions Reference ================================ .. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c :doc: fbdev helpers -.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c - :export: - .. kernel-doc:: include/drm/drm_fb_helper.h :internal: +.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c + :export: + Framebuffer CMA Helper Functions Reference ========================================== @@ -146,15 +146,15 @@ Bridge Helper Reference Panel Helper Reference ====================== +.. kernel-doc:: drivers/gpu/drm/drm_panel.c + :doc: drm panel + .. kernel-doc:: include/drm/drm_panel.h :internal: .. kernel-doc:: drivers/gpu/drm/drm_panel.c :export: -.. kernel-doc:: drivers/gpu/drm/drm_panel.c - :doc: drm panel - Display Port Helper Functions Reference ======================================= diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index bcf475f86e830..b11c537c73f42 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -152,12 +152,12 @@ panels are still in-flux and not really fully sorted out yet. KMS Core Structures and Functions ================================= -.. kernel-doc:: drivers/gpu/drm/drm_mode_config.c - :export: - .. kernel-doc:: include/drm/drm_mode_config.h :internal: +.. kernel-doc:: drivers/gpu/drm/drm_mode_config.c + :export: + Modeset Base Object Abstraction =============================== @@ -170,12 +170,12 @@ Modeset Base Object Abstraction Atomic Mode Setting Function Reference ====================================== -.. kernel-doc:: drivers/gpu/drm/drm_atomic.c - :export: - .. kernel-doc:: include/drm/drm_atomic.h :internal: +.. kernel-doc:: drivers/gpu/drm/drm_atomic.c + :export: + CRTC Abstraction ================ @@ -200,12 +200,12 @@ Frame Buffer Abstraction Frame Buffer Functions Reference -------------------------------- -.. kernel-doc:: drivers/gpu/drm/drm_framebuffer.c - :export: - .. kernel-doc:: include/drm/drm_framebuffer.h :internal: +.. kernel-doc:: drivers/gpu/drm/drm_framebuffer.c + :export: + DRM Format Handling =================== @@ -508,8 +508,8 @@ operation handler. Vertical Blanking and Interrupt Handling Functions Reference ------------------------------------------------------------ -.. kernel-doc:: drivers/gpu/drm/drm_irq.c - :export: - .. kernel-doc:: include/drm/drm_irq.h :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_irq.c + :export: diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index d7a29d41789f0..96b9c34c21e4a 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -365,36 +365,36 @@ from the client in libdrm. GEM Function Reference ---------------------- -.. kernel-doc:: drivers/gpu/drm/drm_gem.c - :export: - .. kernel-doc:: include/drm/drm_gem.h :internal: +.. kernel-doc:: drivers/gpu/drm/drm_gem.c + :export: + GEM CMA Helper Functions Reference ---------------------------------- .. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c :doc: cma helpers -.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c - :export: - .. kernel-doc:: include/drm/drm_gem_cma_helper.h :internal: +.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c + :export: + VMA Offset Manager ================== .. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c :doc: vma offset manager -.. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c - :export: - .. kernel-doc:: include/drm/drm_vma_manager.h :internal: +.. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c + :export: + PRIME Buffer Sharing ==================== @@ -473,12 +473,12 @@ LRU Scan/Eviction Support DRM MM Range Allocator Function References ------------------------------------------ -.. kernel-doc:: drivers/gpu/drm/drm_mm.c - :export: - .. kernel-doc:: include/drm/drm_mm.h :internal: +.. kernel-doc:: drivers/gpu/drm/drm_mm.c + :export: + DRM Cache Handling ================== From b2b82c26c7c1702cacde241cf63137eddd6524bf Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 2 Mar 2017 16:16:37 +0100 Subject: [PATCH 20/37] drm/doc: diagram for mode objects and properties Resulted in confusion a few times in the past. v2: Spelling fix (Eric). Cc: Eric Anholt Acked-by: Eric Anholt Cc: Laurent Pinchart Cc: Manasi Navare Reviewed-by: Gabriel Krisman Bertazi Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170302151638.1882-5-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-kms.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index b11c537c73f42..1c42448c7aae9 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -161,6 +161,28 @@ KMS Core Structures and Functions 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 +`. 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 `, 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() +`. + .. kernel-doc:: include/drm/drm_mode_object.h :internal: From 4a8e2292874e57ab6245ee16a8048729423d289d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 2 Mar 2017 16:16:38 +0100 Subject: [PATCH 21/37] drm/doc: atomic overview, with graph I want to split up a few more things and document some details better (like how exactly to subclass drm_atomic_state). And maybe also split up the helpers a bit per-topic, but this should be a ok-ish start for better atomic overview. v2: Spelling and clarifications (Eric). v3: Implement suggestion from Gabriel to fix the graph. v4: Review from Laurent. Reviewed-by: Laurent Pinchart Cc: Gabriel Krisman Bertazi Acked-by: Eric Anholt Cc: Eric Anholt Cc: Laurent Pinchart Cc: Harry Wentland Reviewed-by: Gabriel Krisman Bertazi Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170302151638.1882-6-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-kms-helpers.rst | 2 + Documentation/gpu/drm-kms.rst | 84 ++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 050ebe81d256b..ac53c0b893f68 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -42,6 +42,8 @@ Modeset Helper Reference for Common Vtables .. kernel-doc:: include/drm/drm_modeset_helper_vtables.h :internal: +.. _drm_atomic_helper: + Atomic Modeset Helper Functions Reference ========================================= diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 1c42448c7aae9..bfecd21a8cdf4 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -189,8 +189,90 @@ multiple times to different objects using :c:func:`drm_object_attach_property() .. kernel-doc:: drivers/gpu/drm/drm_mode_object.c :export: +Atomic Mode Setting +=================== + + +.. 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 ` for planes, :c:type:`struct + drm_crtc_state ` for CRTCs and :c:type:`struct + 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 ` + container. Again drivers can subclass that container for their own state + structure tracking needs. Only when a state is committed is it applied to the + driver and modeset objects. This way rolling back an update boils down to + releasing memory and unreferencing objects like framebuffers. + +Read on in this chapter, and also in :ref:`drm_atomic_helper` for more detailed +coverage of specific topics. + Atomic Mode Setting Function Reference -====================================== +-------------------------------------- .. kernel-doc:: include/drm/drm_atomic.h :internal: From 608b20506941969ea30d8c08dc9ae02bb87dbf7d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 15 Mar 2017 20:40:25 +0000 Subject: [PATCH 22/37] drm: Defer disabling the vblank IRQ until the next interrupt (for instant-off) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On vblank instant-off systems, we can get into a situation where the cost of enabling and disabling the vblank IRQ around a drmWaitVblank query dominates. And with the advent of even deeper hardware sleep state, touching registers becomes ever more expensive. However, we know that if the user wants the current vblank counter, they are also very likely to immediately queue a vblank wait and so we can keep the interrupt around and only turn it off if we have no further vblank requests queued within the interrupt interval. After vblank event delivery, this patch adds a shadow of one vblank where the interrupt is kept alive for the user to query and queue another vblank event. Similarly, if the user is using blocking drmWaitVblanks, the interrupt will be disabled on the IRQ following the wait completion. However, if the user is simply querying the current vblank counter and timestamp, the interrupt will be disabled after every IRQ and the user will enabled it again on the first query following the IRQ. v2: Mario Kleiner - After testing this, one more thing that would make sense is to move the disable block at the end of drm_handle_vblank() instead of at the top. Turns out that if high precision timestaming is disabled or doesn't work for some reason (as can be simulated by echo 0 > /sys/module/drm/parameters/timestamp_precision_usec), then with your delayed disable code at its current place, the vblank counter won't increment anymore at all for instant queries, ie. with your other "instant query" patches. Clients which repeatedly query the counter and wait for it to progress will simply hang, spinning in an endless query loop. There's that comment in vblank_disable_and_save: "* Skip this step if there isn't any high precision timestamp * available. In that case we can't account for this and just * hope for the best. */ With the disable happening after leading edge of vblank (== hw counter increment already happened) but before the vblank counter/timestamp handling in drm_handle_vblank, that step is needed to keep the counter progressing, so skipping it is bad. Now without high precision timestamping support, a kms driver must not set dev->vblank_disable_immediate = true, as this would cause problems for clients, so this shouldn't matter, but it would be good to still make this robust against a future kms driver which might have unreliable high precision timestamping, e.g., high precision timestamping that intermittently doesn't work. v3: Patch before coffee needs extra coffee. Testcase: igt/kms_vblank Signed-off-by: Chris Wilson Cc: Ville Syrjälä Cc: Daniel Vetter Cc: Michel Dänzer Cc: Laurent Pinchart Cc: Dave Airlie , Cc: Mario Kleiner Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170315204027.20160-1-chris@chris-wilson.co.uk --- drivers/gpu/drm/drm_irq.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 9bdca69f754cd..e64b05ea95ea6 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1198,9 +1198,9 @@ static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) if (atomic_dec_and_test(&vblank->refcount)) { if (drm_vblank_offdelay == 0) return; - else if (dev->vblank_disable_immediate || drm_vblank_offdelay < 0) + else if (drm_vblank_offdelay < 0) vblank_disable_fn((unsigned long)vblank); - else + else if (!dev->vblank_disable_immediate) mod_timer(&vblank->disable_timer, jiffies + ((drm_vblank_offdelay * HZ)/1000)); } @@ -1734,6 +1734,16 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) wake_up(&vblank->queue); drm_handle_vblank_events(dev, pipe); + /* With instant-off, we defer disabling the interrupt until after + * we finish processing the following vblank. The disable has to + * be last (after drm_handle_vblank_events) so that the timestamp + * is always accurate. + */ + if (dev->vblank_disable_immediate && + drm_vblank_offdelay > 0 && + !atomic_read(&vblank->refcount)) + vblank_disable_fn((unsigned long)vblank); + spin_unlock_irqrestore(&dev->event_lock, irqflags); return true; From 82c8e025b577a4b9b87fbbc2a1c4dd401e3a2521 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 15 Mar 2017 20:40:26 +0000 Subject: [PATCH 23/37] drm: Skip the waitqueue setup for vblank queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid adding to the waitqueue and reprobing the current vblank if the caller is only querying the current vblank sequence and timestamp, where we know that the wait would return immediately. v2: Add CRTC identifier to debug messages Signed-off-by: Chris Wilson Cc: Ville Syrjälä Cc: Daniel Vetter Cc: Michel Dänzer Cc: Laurent Pinchart Cc: Dave Airlie , Cc: Mario Kleiner Reviewed-by: Michel Dänzer Reviewed-and-tested-by: Mario Kleiner Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170315204027.20160-2-chris@chris-wilson.co.uk --- drivers/gpu/drm/drm_irq.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index e64b05ea95ea6..53a526c7b24dc 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1610,7 +1610,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, ret = drm_vblank_get(dev, pipe); if (ret) { - DRM_DEBUG("failed to acquire vblank counter, %d\n", ret); + DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret); return ret; } seq = drm_vblank_count(dev, pipe); @@ -1638,13 +1638,15 @@ int drm_wait_vblank(struct drm_device *dev, void *data, return drm_queue_vblank_event(dev, pipe, vblwait, file_priv); } - DRM_DEBUG("waiting on vblank count %u, crtc %u\n", - vblwait->request.sequence, pipe); - DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, - (((drm_vblank_count(dev, pipe) - - vblwait->request.sequence) <= (1 << 23)) || - !vblank->enabled || - !dev->irq_enabled)); + if (vblwait->request.sequence != seq) { + DRM_DEBUG("waiting on vblank count %u, crtc %u\n", + vblwait->request.sequence, pipe); + DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, + (((drm_vblank_count(dev, pipe) - + vblwait->request.sequence) <= (1 << 23)) || + !vblank->enabled || + !dev->irq_enabled)); + } if (ret != -EINTR) { struct timeval now; @@ -1653,10 +1655,10 @@ int drm_wait_vblank(struct drm_device *dev, void *data, vblwait->reply.tval_sec = now.tv_sec; vblwait->reply.tval_usec = now.tv_usec; - DRM_DEBUG("returning %u to client\n", - vblwait->reply.sequence); + DRM_DEBUG("crtc %d returning %u to client\n", + pipe, vblwait->reply.sequence); } else { - DRM_DEBUG("vblank wait interrupted by signal\n"); + DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe); } done: From 99ed4d7eb2870ef114b3a1309836d021005e4292 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 1 Mar 2017 13:31:01 +0100 Subject: [PATCH 24/37] drm/atmel-hlcdc: Fix suspend/resume implementation The current suspend resume implementation is assuming register values are kept when entering suspend, which is no longer the case with the suspend-to-RAM on the sama5d2. While at it, switch to the generic infrastructure to enter suspend mode (drm_atomic_helper_suspend/resume()). Signed-off-by: Boris Brezillon Acked-by: Daniel Vetter Tested-by: Sylvain Rochet Link: http://patchwork.freedesktop.org/patch/msgid/1488371461-22243-1-git-send-email-boris.brezillon@free-electrons.com --- .../gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 33 ------------------- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 31 ++++++++--------- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 8 +++-- 3 files changed, 21 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 6b50fb706c0e6..53bfa56ca47aa 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -55,14 +55,12 @@ drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state) * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device * @event: pointer to the current page flip event * @id: CRTC id (returned by drm_crtc_index) - * @enabled: CRTC state */ struct atmel_hlcdc_crtc { struct drm_crtc base; struct atmel_hlcdc_dc *dc; struct drm_pending_vblank_event *event; int id; - bool enabled; }; static inline struct atmel_hlcdc_crtc * @@ -158,9 +156,6 @@ static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) struct regmap *regmap = crtc->dc->hlcdc->regmap; unsigned int status; - if (!crtc->enabled) - return; - drm_crtc_vblank_off(c); pm_runtime_get_sync(dev->dev); @@ -186,8 +181,6 @@ static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) pm_runtime_allow(dev->dev); pm_runtime_put_sync(dev->dev); - - crtc->enabled = false; } static void atmel_hlcdc_crtc_enable(struct drm_crtc *c) @@ -197,9 +190,6 @@ static void atmel_hlcdc_crtc_enable(struct drm_crtc *c) struct regmap *regmap = crtc->dc->hlcdc->regmap; unsigned int status; - if (crtc->enabled) - return; - pm_runtime_get_sync(dev->dev); pm_runtime_forbid(dev->dev); @@ -226,29 +216,6 @@ static void atmel_hlcdc_crtc_enable(struct drm_crtc *c) pm_runtime_put_sync(dev->dev); drm_crtc_vblank_on(c); - - crtc->enabled = true; -} - -void atmel_hlcdc_crtc_suspend(struct drm_crtc *c) -{ - struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); - - if (crtc->enabled) { - atmel_hlcdc_crtc_disable(c); - /* save enable state for resume */ - crtc->enabled = true; - } -} - -void atmel_hlcdc_crtc_resume(struct drm_crtc *c) -{ - struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); - - if (crtc->enabled) { - crtc->enabled = false; - atmel_hlcdc_crtc_enable(c); - } } #define ATMEL_HLCDC_RGB444_OUTPUT BIT(0) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 178acbf5f6c53..f4a3065f7f517 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -800,31 +800,32 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) static int atmel_hlcdc_dc_drm_suspend(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); - struct drm_crtc *crtc; + struct atmel_hlcdc_dc *dc = drm_dev->dev_private; + struct regmap *regmap = dc->hlcdc->regmap; + struct drm_atomic_state *state; + + state = drm_atomic_helper_suspend(drm_dev); + if (IS_ERR(state)) + return PTR_ERR(state); - if (pm_runtime_suspended(dev)) - return 0; + dc->suspend.state = state; + + regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr); + regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr); + clk_disable_unprepare(dc->hlcdc->periph_clk); - drm_modeset_lock_all(drm_dev); - list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) - atmel_hlcdc_crtc_suspend(crtc); - drm_modeset_unlock_all(drm_dev); return 0; } static int atmel_hlcdc_dc_drm_resume(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); - struct drm_crtc *crtc; + struct atmel_hlcdc_dc *dc = drm_dev->dev_private; - if (pm_runtime_suspended(dev)) - return 0; + clk_prepare_enable(dc->hlcdc->periph_clk); + regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr); - drm_modeset_lock_all(drm_dev); - list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) - atmel_hlcdc_crtc_resume(crtc); - drm_modeset_unlock_all(drm_dev); - return 0; + return drm_atomic_helper_resume(drm_dev, dc->suspend.state); } #endif diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index da7f25a59be58..433641b6e23b5 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -358,6 +358,7 @@ struct atmel_hlcdc_plane_properties { * @planes: instantiated planes * @layers: active HLCDC layers * @wq: display controller workqueue + * @suspend: used to store the HLCDC state when entering suspend * @commit: used for async commit handling */ struct atmel_hlcdc_dc { @@ -368,6 +369,10 @@ struct atmel_hlcdc_dc { struct drm_crtc *crtc; struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS]; struct workqueue_struct *wq; + struct { + u32 imr; + struct drm_atomic_state *state; + } suspend; struct { wait_queue_head_t wait; bool pending; @@ -428,9 +433,6 @@ int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state); void atmel_hlcdc_crtc_irq(struct drm_crtc *c); -void atmel_hlcdc_crtc_suspend(struct drm_crtc *crtc); -void atmel_hlcdc_crtc_resume(struct drm_crtc *crtc); - int atmel_hlcdc_crtc_create(struct drm_device *dev); int atmel_hlcdc_create_outputs(struct drm_device *dev); From 199a76946d4303ce047e6ac9a49eefa0105f6e78 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 27 Feb 2017 12:28:01 -0800 Subject: [PATCH 25/37] dt-bindings: Document the dmas and dma-names properties for VC4 HDMI These are optional, but necessary for HDMI audio support. Signed-off-by: Boris Brezillon Signed-off-by: Eric Anholt Acked-by: Rob Herring Link: http://patchwork.freedesktop.org/patch/msgid/20170227202803.12855-1-eric@anholt.net --- Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt index 34c7fddcea394..ca02d3e4db917 100644 --- a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt +++ b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt @@ -34,6 +34,9 @@ Optional properties for HDMI: - hpd-gpios: The GPIO pin for HDMI hotplug detect (if it doesn't appear as an interrupt/status bit in the HDMI controller itself). See bindings/pinctrl/brcm,bcm2835-gpio.txt +- dmas: Should contain one entry pointing to the DMA channel used to + transfer audio data +- dma-names: Should contain "audio-rx" Required properties for DPI: - compatible: Should be "brcm,bcm2835-dpi" From bb7d78568814a31a11fa14f1479a9fe51f1582ad Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 27 Feb 2017 12:28:02 -0800 Subject: [PATCH 26/37] drm/vc4: Add HDMI audio support The HDMI encoder IP embeds all needed blocks to output audio, with a custom DAI called MAI moving audio between the two parts of the HDMI core. This driver now exposes a sound card to let users stream audio to their display. Using the hdmi-codec driver has been considered here, but MAI meant having to significantly rework hdmi-codec, and it would have left little shared code with the I2S mode anyway. The encoder requires that the audio be SPDIF-formatted frames only, which alsalib will format-convert for us. This patch is the combined work of Eric Anholt (initial register setup with a separate dmaengine driver and using simple-audio-card) and Boris Brezillon (moving it all into HDMI, massive debug to get it actually working), and which Eric has the permission to release. v2: Drop "-audio" from sound card name, since that's already implied (suggestion by Boris) Signed-off-by: Eric Anholt Acked-by: Boris Brezillon Link: http://patchwork.freedesktop.org/patch/msgid/20170227202803.12855-2-eric@anholt.net --- drivers/gpu/drm/vc4/Kconfig | 4 + drivers/gpu/drm/vc4/vc4_hdmi.c | 494 ++++++++++++++++++++++++++++++++- drivers/gpu/drm/vc4/vc4_regs.h | 107 ++++++- 3 files changed, 603 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index e1517d07cb7d2..973b4203c0b26 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -2,11 +2,15 @@ config DRM_VC4 tristate "Broadcom VC4 Graphics" depends on ARCH_BCM2835 || COMPILE_TEST depends on DRM + depends on SND && SND_SOC depends on COMMON_CLK select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER select DRM_PANEL + select SND_PCM + select SND_PCM_ELD + select SND_SOC_GENERIC_DMAENGINE_PCM select DRM_MIPI_DSI help Choose this option if you have a system that has a Broadcom diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 1be1e83047207..e9cbe269710bc 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -48,11 +48,27 @@ #include "linux/clk.h" #include "linux/component.h" #include "linux/i2c.h" +#include "linux/of_address.h" #include "linux/of_gpio.h" #include "linux/of_platform.h" +#include "linux/rational.h" +#include "sound/dmaengine_pcm.h" +#include "sound/pcm_drm_eld.h" +#include "sound/pcm_params.h" +#include "sound/soc.h" #include "vc4_drv.h" #include "vc4_regs.h" +/* HDMI audio information */ +struct vc4_hdmi_audio { + struct snd_soc_card card; + struct snd_soc_dai_link link; + int samplerate; + int channels; + struct snd_dmaengine_dai_dma_data dma_data; + struct snd_pcm_substream *substream; +}; + /* General HDMI hardware state. */ struct vc4_hdmi { struct platform_device *pdev; @@ -60,6 +76,8 @@ struct vc4_hdmi { struct drm_encoder *encoder; struct drm_connector *connector; + struct vc4_hdmi_audio audio; + struct i2c_adapter *ddc; void __iomem *hdmicore_regs; void __iomem *hd_regs; @@ -115,6 +133,10 @@ static const struct { HDMI_REG(VC4_HDMI_SW_RESET_CONTROL), HDMI_REG(VC4_HDMI_HOTPLUG_INT), HDMI_REG(VC4_HDMI_HOTPLUG), + HDMI_REG(VC4_HDMI_MAI_CHANNEL_MAP), + HDMI_REG(VC4_HDMI_MAI_CONFIG), + HDMI_REG(VC4_HDMI_MAI_FORMAT), + HDMI_REG(VC4_HDMI_AUDIO_PACKET_CONFIG), HDMI_REG(VC4_HDMI_RAM_PACKET_CONFIG), HDMI_REG(VC4_HDMI_HORZA), HDMI_REG(VC4_HDMI_HORZB), @@ -125,6 +147,7 @@ static const struct { HDMI_REG(VC4_HDMI_VERTB0), HDMI_REG(VC4_HDMI_VERTB1), HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL), + HDMI_REG(VC4_HDMI_TX_PHY_CTL0), }; static const struct { @@ -133,6 +156,9 @@ static const struct { } hd_regs[] = { HDMI_REG(VC4_HD_M_CTL), HDMI_REG(VC4_HD_MAI_CTL), + HDMI_REG(VC4_HD_MAI_THR), + HDMI_REG(VC4_HD_MAI_FMT), + HDMI_REG(VC4_HD_MAI_SMP), HDMI_REG(VC4_HD_VID_CTL), HDMI_REG(VC4_HD_CSC_CTL), HDMI_REG(VC4_HD_FRAME_COUNT), @@ -232,6 +258,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); + drm_edid_to_eld(connector, edid); return ret; } @@ -317,7 +344,7 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); u32 packet_id = frame->any.type - 0x80; - u32 packet_reg = VC4_HDMI_GCP_0 + VC4_HDMI_PACKET_STRIDE * packet_id; + u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id); uint8_t buffer[VC4_HDMI_PACKET_STRIDE]; ssize_t len, i; int ret; @@ -398,6 +425,24 @@ static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder) vc4_hdmi_write_infoframe(encoder, &frame); } +static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder) +{ + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = drm->dev_private; + struct vc4_hdmi *hdmi = vc4->hdmi; + union hdmi_infoframe frame; + int ret; + + ret = hdmi_audio_infoframe_init(&frame.audio); + + frame.audio.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; + frame.audio.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + frame.audio.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; + frame.audio.channels = hdmi->audio.channels; + + vc4_hdmi_write_infoframe(encoder, &frame); +} + static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) { vc4_hdmi_set_avi_infoframe(encoder); @@ -606,6 +651,447 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { .enable = vc4_hdmi_encoder_enable, }; +/* HDMI audio codec callbacks */ +static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *hdmi) +{ + struct drm_device *drm = hdmi->encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + u32 hsm_clock = clk_get_rate(hdmi->hsm_clock); + unsigned long n, m; + + rational_best_approximation(hsm_clock, hdmi->audio.samplerate, + VC4_HD_MAI_SMP_N_MASK >> + VC4_HD_MAI_SMP_N_SHIFT, + (VC4_HD_MAI_SMP_M_MASK >> + VC4_HD_MAI_SMP_M_SHIFT) + 1, + &n, &m); + + HD_WRITE(VC4_HD_MAI_SMP, + VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) | + VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M)); +} + +static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi) +{ + struct drm_encoder *encoder = hdmi->encoder; + struct drm_crtc *crtc = encoder->crtc; + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + const struct drm_display_mode *mode = &crtc->state->adjusted_mode; + u32 samplerate = hdmi->audio.samplerate; + u32 n, cts; + u64 tmp; + + n = 128 * samplerate / 1000; + tmp = (u64)(mode->clock * 1000) * n; + do_div(tmp, 128 * samplerate); + cts = tmp; + + HDMI_WRITE(VC4_HDMI_CRP_CFG, + VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN | + VC4_SET_FIELD(n, VC4_HDMI_CRP_CFG_N)); + + /* + * We could get slightly more accurate clocks in some cases by + * providing a CTS_1 value. The two CTS values are alternated + * between based on the period fields + */ + HDMI_WRITE(VC4_HDMI_CTS_0, cts); + HDMI_WRITE(VC4_HDMI_CTS_1, cts); +} + +static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai); + + return snd_soc_card_get_drvdata(card); +} + +static int vc4_hdmi_audio_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = hdmi->encoder; + struct vc4_dev *vc4 = to_vc4_dev(encoder->dev); + int ret; + + if (hdmi->audio.substream && hdmi->audio.substream != substream) + return -EINVAL; + + hdmi->audio.substream = substream; + + /* + * If the HDMI encoder hasn't probed, or the encoder is + * currently in DVI mode, treat the codec dai as missing. + */ + if (!encoder->crtc || !(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & + VC4_HDMI_RAM_PACKET_ENABLE)) + return -ENODEV; + + ret = snd_pcm_hw_constraint_eld(substream->runtime, + hdmi->connector->eld); + if (ret) + return ret; + + return 0; +} + +static int vc4_hdmi_audio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + return 0; +} + +static void vc4_hdmi_audio_reset(struct vc4_hdmi *hdmi) +{ + struct drm_encoder *encoder = hdmi->encoder; + struct drm_device *drm = encoder->dev; + struct device *dev = &hdmi->pdev->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + int ret; + + ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO); + if (ret) + dev_err(dev, "Failed to stop audio infoframe: %d\n", ret); + + HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_RESET); + HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_ERRORF); + HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_FLUSH); +} + +static void vc4_hdmi_audio_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); + + if (substream != hdmi->audio.substream) + return; + + vc4_hdmi_audio_reset(hdmi); + + hdmi->audio.substream = NULL; +} + +/* HDMI audio codec callbacks */ +static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = hdmi->encoder; + struct drm_device *drm = encoder->dev; + struct device *dev = &hdmi->pdev->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + u32 audio_packet_config, channel_mask; + u32 channel_map, i; + + if (substream != hdmi->audio.substream) + return -EINVAL; + + dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__, + params_rate(params), params_width(params), + params_channels(params)); + + hdmi->audio.channels = params_channels(params); + hdmi->audio.samplerate = params_rate(params); + + HD_WRITE(VC4_HD_MAI_CTL, + VC4_HD_MAI_CTL_RESET | + VC4_HD_MAI_CTL_FLUSH | + VC4_HD_MAI_CTL_DLATE | + VC4_HD_MAI_CTL_ERRORE | + VC4_HD_MAI_CTL_ERRORF); + + vc4_hdmi_audio_set_mai_clock(hdmi); + + audio_packet_config = + VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT | + VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS | + VC4_SET_FIELD(0xf, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER); + + channel_mask = GENMASK(hdmi->audio.channels - 1, 0); + audio_packet_config |= VC4_SET_FIELD(channel_mask, + VC4_HDMI_AUDIO_PACKET_CEA_MASK); + + /* Set the MAI threshold. This logic mimics the firmware's. */ + if (hdmi->audio.samplerate > 96000) { + HD_WRITE(VC4_HD_MAI_THR, + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW)); + } else if (hdmi->audio.samplerate > 48000) { + HD_WRITE(VC4_HD_MAI_THR, + VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW)); + } else { + HD_WRITE(VC4_HD_MAI_THR, + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) | + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) | + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW)); + } + + HDMI_WRITE(VC4_HDMI_MAI_CONFIG, + VC4_HDMI_MAI_CONFIG_BIT_REVERSE | + VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK)); + + channel_map = 0; + for (i = 0; i < 8; i++) { + if (channel_mask & BIT(i)) + channel_map |= i << (3 * i); + } + + HDMI_WRITE(VC4_HDMI_MAI_CHANNEL_MAP, channel_map); + HDMI_WRITE(VC4_HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); + vc4_hdmi_set_n_cts(hdmi); + + return 0; +} + +static int vc4_hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = hdmi->encoder; + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + vc4_hdmi_set_audio_infoframe(encoder); + HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0, + HDMI_READ(VC4_HDMI_TX_PHY_CTL0) & + ~VC4_HDMI_TX_PHY_RNG_PWRDN); + HD_WRITE(VC4_HD_MAI_CTL, + VC4_SET_FIELD(hdmi->audio.channels, + VC4_HD_MAI_CTL_CHNUM) | + VC4_HD_MAI_CTL_ENABLE); + break; + case SNDRV_PCM_TRIGGER_STOP: + HD_WRITE(VC4_HD_MAI_CTL, + VC4_HD_MAI_CTL_DLATE | + VC4_HD_MAI_CTL_ERRORE | + VC4_HD_MAI_CTL_ERRORF); + HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0, + HDMI_READ(VC4_HDMI_TX_PHY_CTL0) | + VC4_HDMI_TX_PHY_RNG_PWRDN); + break; + default: + break; + } + + return 0; +} + +static inline struct vc4_hdmi * +snd_component_to_hdmi(struct snd_soc_component *component) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(component); + + return snd_soc_card_get_drvdata(card); +} + +static int vc4_hdmi_audio_eld_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct vc4_hdmi *hdmi = snd_component_to_hdmi(component); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(hdmi->connector->eld); + + return 0; +} + +static int vc4_hdmi_audio_eld_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct vc4_hdmi *hdmi = snd_component_to_hdmi(component); + + memcpy(ucontrol->value.bytes.data, hdmi->connector->eld, + sizeof(hdmi->connector->eld)); + + return 0; +} + +static const struct snd_kcontrol_new vc4_hdmi_audio_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = vc4_hdmi_audio_eld_ctl_info, + .get = vc4_hdmi_audio_eld_ctl_get, + }, +}; + +static const struct snd_soc_dapm_widget vc4_hdmi_audio_widgets[] = { + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route vc4_hdmi_audio_routes[] = { + { "TX", NULL, "Playback" }, +}; + +static const struct snd_soc_codec_driver vc4_hdmi_audio_codec_drv = { + .component_driver = { + .controls = vc4_hdmi_audio_controls, + .num_controls = ARRAY_SIZE(vc4_hdmi_audio_controls), + .dapm_widgets = vc4_hdmi_audio_widgets, + .num_dapm_widgets = ARRAY_SIZE(vc4_hdmi_audio_widgets), + .dapm_routes = vc4_hdmi_audio_routes, + .num_dapm_routes = ARRAY_SIZE(vc4_hdmi_audio_routes), + }, +}; + +static const struct snd_soc_dai_ops vc4_hdmi_audio_dai_ops = { + .startup = vc4_hdmi_audio_startup, + .shutdown = vc4_hdmi_audio_shutdown, + .hw_params = vc4_hdmi_audio_hw_params, + .set_fmt = vc4_hdmi_audio_set_fmt, + .trigger = vc4_hdmi_audio_trigger, +}; + +static struct snd_soc_dai_driver vc4_hdmi_audio_codec_dai_drv = { + .name = "vc4-hdmi-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, + }, +}; + +static const struct snd_soc_component_driver vc4_hdmi_audio_cpu_dai_comp = { + .name = "vc4-hdmi-cpu-dai-component", +}; + +static int vc4_hdmi_audio_cpu_dai_probe(struct snd_soc_dai *dai) +{ + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); + + snd_soc_dai_init_dma_data(dai, &hdmi->audio.dma_data, NULL); + + return 0; +} + +static struct snd_soc_dai_driver vc4_hdmi_audio_cpu_dai_drv = { + .name = "vc4-hdmi-cpu-dai", + .probe = vc4_hdmi_audio_cpu_dai_probe, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, + }, + .ops = &vc4_hdmi_audio_dai_ops, +}; + +static const struct snd_dmaengine_pcm_config pcm_conf = { + .chan_names[SNDRV_PCM_STREAM_PLAYBACK] = "audio-rx", + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +}; + +static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi) +{ + struct snd_soc_dai_link *dai_link = &hdmi->audio.link; + struct snd_soc_card *card = &hdmi->audio.card; + struct device *dev = &hdmi->pdev->dev; + const __be32 *addr; + int ret; + + if (!of_find_property(dev->of_node, "dmas", NULL)) { + dev_warn(dev, + "'dmas' DT property is missing, no HDMI audio\n"); + return 0; + } + + /* + * Get the physical address of VC4_HD_MAI_DATA. We need to retrieve + * the bus address specified in the DT, because the physical address + * (the one returned by platform_get_resource()) is not appropriate + * for DMA transfers. + * This VC/MMU should probably be exposed to avoid this kind of hacks. + */ + addr = of_get_address(dev->of_node, 1, NULL, NULL); + hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA; + hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + hdmi->audio.dma_data.maxburst = 2; + + ret = devm_snd_dmaengine_pcm_register(dev, &pcm_conf, 0); + if (ret) { + dev_err(dev, "Could not register PCM component: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, &vc4_hdmi_audio_cpu_dai_comp, + &vc4_hdmi_audio_cpu_dai_drv, 1); + if (ret) { + dev_err(dev, "Could not register CPU DAI: %d\n", ret); + return ret; + } + + /* register codec and codec dai */ + ret = snd_soc_register_codec(dev, &vc4_hdmi_audio_codec_drv, + &vc4_hdmi_audio_codec_dai_drv, 1); + if (ret) { + dev_err(dev, "Could not register codec: %d\n", ret); + return ret; + } + + dai_link->name = "MAI"; + dai_link->stream_name = "MAI PCM"; + dai_link->codec_dai_name = vc4_hdmi_audio_codec_dai_drv.name; + dai_link->cpu_dai_name = dev_name(dev); + dai_link->codec_name = dev_name(dev); + dai_link->platform_name = dev_name(dev); + + card->dai_link = dai_link; + card->num_links = 1; + card->name = "vc4-hdmi"; + card->dev = dev; + + /* + * Be careful, snd_soc_register_card() calls dev_set_drvdata() and + * stores a pointer to the snd card object in dev->driver_data. This + * means we cannot use it for something else. The hdmi back-pointer is + * now stored in card->drvdata and should be retrieved with + * snd_soc_card_get_drvdata() if needed. + */ + snd_soc_card_set_drvdata(card, hdmi); + ret = devm_snd_soc_register_card(dev, card); + if (ret) { + dev_err(dev, "Could not register sound card: %d\n", ret); + goto unregister_codec; + } + + return 0; + +unregister_codec: + snd_soc_unregister_codec(dev); + + return ret; +} + +static void vc4_hdmi_audio_cleanup(struct vc4_hdmi *hdmi) +{ + struct device *dev = &hdmi->pdev->dev; + + /* + * If drvdata is not set this means the audio card was not + * registered, just skip codec unregistration in this case. + */ + if (dev_get_drvdata(dev)) + snd_soc_unregister_codec(dev); +} + static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -737,6 +1223,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) goto err_destroy_encoder; } + ret = vc4_hdmi_audio_init(hdmi); + if (ret) + goto err_destroy_encoder; + return 0; err_destroy_encoder: @@ -758,6 +1248,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, struct vc4_dev *vc4 = drm->dev_private; struct vc4_hdmi *hdmi = vc4->hdmi; + vc4_hdmi_audio_cleanup(hdmi); + vc4_hdmi_connector_destroy(hdmi->connector); vc4_hdmi_encoder_destroy(hdmi->encoder); diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 385405a2df05e..9320939361786 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -446,11 +446,62 @@ #define VC4_HDMI_HOTPLUG 0x00c # define VC4_HDMI_HOTPLUG_CONNECTED BIT(0) +/* 3 bits per field, where each field maps from that corresponding MAI + * bus channel to the given HDMI channel. + */ +#define VC4_HDMI_MAI_CHANNEL_MAP 0x090 + +#define VC4_HDMI_MAI_CONFIG 0x094 +# define VC4_HDMI_MAI_CONFIG_FORMAT_REVERSE BIT(27) +# define VC4_HDMI_MAI_CONFIG_BIT_REVERSE BIT(26) +# define VC4_HDMI_MAI_CHANNEL_MASK_MASK VC4_MASK(15, 0) +# define VC4_HDMI_MAI_CHANNEL_MASK_SHIFT 0 + +/* Last received format word on the MAI bus. */ +#define VC4_HDMI_MAI_FORMAT 0x098 + +#define VC4_HDMI_AUDIO_PACKET_CONFIG 0x09c +# define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT BIT(29) +# define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS BIT(24) +# define VC4_HDMI_AUDIO_PACKET_FORCE_SAMPLE_PRESENT BIT(19) +# define VC4_HDMI_AUDIO_PACKET_FORCE_B_FRAME BIT(18) +# define VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER_MASK VC4_MASK(13, 10) +# define VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER_SHIFT 10 +/* If set, then multichannel, otherwise 2 channel. */ +# define VC4_HDMI_AUDIO_PACKET_AUDIO_LAYOUT BIT(9) +/* If set, then AUDIO_LAYOUT overrides audio_cea_mask */ +# define VC4_HDMI_AUDIO_PACKET_FORCE_AUDIO_LAYOUT BIT(8) +# define VC4_HDMI_AUDIO_PACKET_CEA_MASK_MASK VC4_MASK(7, 0) +# define VC4_HDMI_AUDIO_PACKET_CEA_MASK_SHIFT 0 + #define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0 # define VC4_HDMI_RAM_PACKET_ENABLE BIT(16) #define VC4_HDMI_RAM_PACKET_STATUS 0x0a4 +#define VC4_HDMI_CRP_CFG 0x0a8 +/* When set, the CTS_PERIOD counts based on MAI bus sync pulse instead + * of pixel clock. + */ +# define VC4_HDMI_CRP_USE_MAI_BUS_SYNC_FOR_CTS BIT(26) +/* When set, no CRP packets will be sent. */ +# define VC4_HDMI_CRP_CFG_DISABLE BIT(25) +/* If set, generates CTS values based on N, audio clock, and video + * clock. N must be divisible by 128. + */ +# define VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN BIT(24) +# define VC4_HDMI_CRP_CFG_N_MASK VC4_MASK(19, 0) +# define VC4_HDMI_CRP_CFG_N_SHIFT 0 + +/* 20-bit fields containing CTS values to be transmitted if !EXTERNAL_CTS_EN */ +#define VC4_HDMI_CTS_0 0x0ac +#define VC4_HDMI_CTS_1 0x0b0 +/* 20-bit fields containing number of clocks to send CTS0/1 before + * switching to the other one. + */ +#define VC4_HDMI_CTS_PERIOD_0 0x0b4 +#define VC4_HDMI_CTS_PERIOD_1 0x0b8 + #define VC4_HDMI_HORZA 0x0c4 # define VC4_HDMI_HORZA_VPOS BIT(14) # define VC4_HDMI_HORZA_HPOS BIT(13) @@ -512,7 +563,11 @@ #define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 -#define VC4_HDMI_GCP_0 0x400 +#define VC4_HDMI_TX_PHY_CTL0 0x2c4 +# define VC4_HDMI_TX_PHY_RNG_PWRDN BIT(25) + +#define VC4_HDMI_GCP(x) (0x400 + ((x) * 0x4)) +#define VC4_HDMI_RAM_PACKET(x) (0x400 + ((x) * 0x24)) #define VC4_HDMI_PACKET_STRIDE 0x24 #define VC4_HD_M_CTL 0x00c @@ -522,6 +577,56 @@ # define VC4_HD_M_ENABLE BIT(0) #define VC4_HD_MAI_CTL 0x014 +/* Set when audio stream is received at a slower rate than the + * sampling period, so MAI fifo goes empty. Write 1 to clear. + */ +# define VC4_HD_MAI_CTL_DLATE BIT(15) +# define VC4_HD_MAI_CTL_BUSY BIT(14) +# define VC4_HD_MAI_CTL_CHALIGN BIT(13) +# define VC4_HD_MAI_CTL_WHOLSMP BIT(12) +# define VC4_HD_MAI_CTL_FULL BIT(11) +# define VC4_HD_MAI_CTL_EMPTY BIT(10) +# define VC4_HD_MAI_CTL_FLUSH BIT(9) +/* If set, MAI bus generates SPDIF (bit 31) parity instead of passing + * through. + */ +# define VC4_HD_MAI_CTL_PAREN BIT(8) +# define VC4_HD_MAI_CTL_CHNUM_MASK VC4_MASK(7, 4) +# define VC4_HD_MAI_CTL_CHNUM_SHIFT 4 +# define VC4_HD_MAI_CTL_ENABLE BIT(3) +/* Underflow error status bit, write 1 to clear. */ +# define VC4_HD_MAI_CTL_ERRORE BIT(2) +/* Overflow error status bit, write 1 to clear. */ +# define VC4_HD_MAI_CTL_ERRORF BIT(1) +/* Single-shot reset bit. Read value is undefined. */ +# define VC4_HD_MAI_CTL_RESET BIT(0) + +#define VC4_HD_MAI_THR 0x018 +# define VC4_HD_MAI_THR_PANICHIGH_MASK VC4_MASK(29, 24) +# define VC4_HD_MAI_THR_PANICHIGH_SHIFT 24 +# define VC4_HD_MAI_THR_PANICLOW_MASK VC4_MASK(21, 16) +# define VC4_HD_MAI_THR_PANICLOW_SHIFT 16 +# define VC4_HD_MAI_THR_DREQHIGH_MASK VC4_MASK(13, 8) +# define VC4_HD_MAI_THR_DREQHIGH_SHIFT 8 +# define VC4_HD_MAI_THR_DREQLOW_MASK VC4_MASK(5, 0) +# define VC4_HD_MAI_THR_DREQLOW_SHIFT 0 + +/* Format header to be placed on the MAI data. Unused. */ +#define VC4_HD_MAI_FMT 0x01c + +/* Register for DMAing in audio data to be transported over the MAI + * bus to the Falcon core. + */ +#define VC4_HD_MAI_DATA 0x020 + +/* Divider from HDMI HSM clock to MAI serial clock. Sampling period + * converges to N / (M + 1) cycles. + */ +#define VC4_HD_MAI_SMP 0x02c +# define VC4_HD_MAI_SMP_N_MASK VC4_MASK(31, 8) +# define VC4_HD_MAI_SMP_N_SHIFT 8 +# define VC4_HD_MAI_SMP_M_MASK VC4_MASK(7, 0) +# define VC4_HD_MAI_SMP_M_SHIFT 0 #define VC4_HD_VID_CTL 0x038 # define VC4_HD_VID_CTL_ENABLE BIT(31) From d5b72a2123dfaf9416b1a1177b4be041f8a8b6d4 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 17 Mar 2017 17:34:49 +0100 Subject: [PATCH 27/37] dma-fence: add dma_fence_match_context helper Add a helper to check if all fences in a fence array are from a given context. For convenience, the function can also handle being given a non-array fence. Signed-off-by: Philipp Zabel Reviewed-by: Gustavo Padovan Acked-by: Sumit Semwal Signed-off-by: Sumit Semwal Link: http://patchwork.freedesktop.org/patch/msgid/1489768492-25190-1-git-send-email-p.zabel@pengutronix.de --- drivers/dma-buf/dma-fence-array.c | 26 ++++++++++++++++++++++++++ include/linux/dma-fence-array.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/drivers/dma-buf/dma-fence-array.c b/drivers/dma-buf/dma-fence-array.c index 67eb7c8fb88c3..0350829ba62e7 100644 --- a/drivers/dma-buf/dma-fence-array.c +++ b/drivers/dma-buf/dma-fence-array.c @@ -144,3 +144,29 @@ struct dma_fence_array *dma_fence_array_create(int num_fences, return array; } EXPORT_SYMBOL(dma_fence_array_create); + +/** + * dma_fence_match_context - Check if all fences are from the given context + * @fence: [in] fence or fence array + * @context: [in] fence context to check all fences against + * + * Checks the provided fence or, for a fence array, all fences in the array + * against the given context. Returns false if any fence is from a different + * context. + */ +bool dma_fence_match_context(struct dma_fence *fence, u64 context) +{ + struct dma_fence_array *array = to_dma_fence_array(fence); + unsigned i; + + if (!dma_fence_is_array(fence)) + return fence->context == context; + + for (i = 0; i < array->num_fences; i++) { + if (array->fences[i]->context != context) + return false; + } + + return true; +} +EXPORT_SYMBOL(dma_fence_match_context); diff --git a/include/linux/dma-fence-array.h b/include/linux/dma-fence-array.h index 5900945f962dd..332a5420243c4 100644 --- a/include/linux/dma-fence-array.h +++ b/include/linux/dma-fence-array.h @@ -83,4 +83,6 @@ struct dma_fence_array *dma_fence_array_create(int num_fences, u64 context, unsigned seqno, bool signal_on_any); +bool dma_fence_match_context(struct dma_fence *fence, u64 context); + #endif /* __LINUX_DMA_FENCE_ARRAY_H */ From cc2e6da1b5a8986dc2831d5b63f4d0a15704e4f1 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 17 Mar 2017 18:00:59 +0100 Subject: [PATCH 28/37] drm/vc4: use platform_register_drivers Use platform_register_drivers instead of open coding the iteration over component platform drivers in the vc4_drv module. Signed-off-by: Philipp Zabel Reviewed-by: Eric Anholt Link: http://patchwork.freedesktop.org/patch/msgid/20170317170059.17821-1-p.zabel@pengutronix.de --- drivers/gpu/drm/vc4/vc4_drv.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 205c1961ffb4c..61e674baf3a6f 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -349,26 +349,20 @@ static struct platform_driver vc4_platform_driver = { static int __init vc4_drm_register(void) { - int i, ret; + int ret; + + ret = platform_register_drivers(component_drivers, + ARRAY_SIZE(component_drivers)); + if (ret) + return ret; - for (i = 0; i < ARRAY_SIZE(component_drivers); i++) { - ret = platform_driver_register(component_drivers[i]); - if (ret) { - while (--i >= 0) - platform_driver_unregister(component_drivers[i]); - return ret; - } - } return platform_driver_register(&vc4_platform_driver); } static void __exit vc4_drm_unregister(void) { - int i; - - for (i = ARRAY_SIZE(component_drivers) - 1; i >= 0; i--) - platform_driver_unregister(component_drivers[i]); - + platform_unregister_drivers(component_drivers, + ARRAY_SIZE(component_drivers)); platform_driver_unregister(&vc4_platform_driver); } From 7b34734816d5170f40329598390de22d0166e4e9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 16 Mar 2017 18:54:18 +0000 Subject: [PATCH 29/37] drm: vc4: remove redundant check of plane being non-null The pointer plane is always null on the error path at label 'fail' hence the check if it is non-null is redundant. We can therefore remove the check and the destruction of plane as well as the fail error path and instead just return an -ENOMEM ERR_PTR. Detected by CoverityScan, CID#1339532 ("Logically Dead Code") Signed-off-by: Colin Ian King Reviewed-by: Eric Anholt Link: http://patchwork.freedesktop.org/patch/msgid/20170316185418.32765-1-colin.king@canonical.com --- drivers/gpu/drm/vc4/vc4_plane.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 110224c3a3ac1..0f4564beb0179 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -842,10 +842,8 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane), GFP_KERNEL); - if (!vc4_plane) { - ret = -ENOMEM; - goto fail; - } + if (!vc4_plane) + return ERR_PTR(-ENOMEM); for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { /* Don't allow YUV in cursor planes, since that means @@ -866,9 +864,4 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, drm_plane_helper_add(plane, &vc4_plane_helper_funcs); return plane; -fail: - if (plane) - vc4_plane_destroy(plane); - - return ERR_PTR(ret); } From 4fa13dbe8c86382a846584e65c47bce09297f75b Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Fri, 17 Mar 2017 15:14:09 -0300 Subject: [PATCH 30/37] drm: bochs: Don't remove uninitialized fbdev framebuffer In the same spirit of the fix for QXL in commit 861078381ba5 ("drm: qxl: Don't alloc fbdev if emulation is not supported"), prevent the Oops in the unbind path of Bochs if fbdev emulation is disabled. [ 112.176009] Oops: 0002 [#1] SMP [ 112.176009] Modules linked in: bochs_drm [ 112.176009] CPU: 0 PID: 3002 Comm: bash Not tainted 4.11.0-rc1+ #111 [ 112.176009] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.9.3-20161025_171302-gandalf 04/01/2014 [ 112.176009] task: ffff8800743bbac0 task.stack: ffffc90000b5c000 [ 112.176009] RIP: 0010:mutex_lock+0x18/0x30 [ 112.176009] RSP: 0018:ffffc90000b5fc78 EFLAGS: 00010246 [ 112.176009] RAX: 0000000000000000 RBX: 0000000000000260 RCX: 0000000000000000 [ 112.176009] RDX: ffff8800743bbac0 RSI: ffff8800787176e0 RDI: 0000000000000260 [ 112.176009] RBP: ffffc90000b5fc80 R08: ffffffff00000000 R09: 00000000ffffffff [ 112.176009] R10: ffff88007b463650 R11: 0000000000000000 R12: 0000000000000260 [ 112.176009] R13: ffff8800787176e0 R14: ffffffffa0003068 R15: 0000000000000060 [ 112.176009] FS: 00007f20564c7b40(0000) GS:ffff88007ce00000(0000) knlGS:0000000000000000 [ 112.176009] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 112.176009] CR2: 0000000000000260 CR3: 000000006b89c000 CR4: 00000000000006f0 [ 112.176009] Call Trace: [ 112.176009] drm_mode_object_unregister+0x1e/0x50 [ 112.176009] drm_framebuffer_unregister_private+0x15/0x20 [ 112.176009] bochs_fbdev_fini+0x57/0x70 [bochs_drm] [ 112.176009] bochs_unload+0x16/0x50 [bochs_drm] [ 112.176009] drm_dev_unregister+0x37/0xd0 [ 112.176009] drm_put_dev+0x31/0x60 [ 112.176009] bochs_pci_remove+0x10/0x20 [bochs_drm] [ 112.176009] pci_device_remove+0x34/0xb0 [ 112.176009] device_release_driver_internal+0x150/0x200 [ 112.176009] device_release_driver+0xd/0x10 [ 112.176009] unbind_store+0x108/0x150 [ 112.176009] drv_attr_store+0x20/0x30 [ 112.176009] sysfs_kf_write+0x32/0x40 [ 112.176009] kernfs_fop_write+0x10b/0x190 [ 112.176009] __vfs_write+0x23/0x120 [ 112.176009] ? security_file_permission+0x36/0xb0 [ 112.176009] ? rw_verify_area+0x49/0xb0 [ 112.176009] vfs_write+0xb0/0x190 [ 112.176009] SyS_write+0x41/0xa0 [ 112.176009] entry_SYSCALL_64_fastpath+0x1a/0xa9 [ 112.176009] RIP: 0033:0x7f2055bd5620 [ 112.176009] RSP: 002b:00007ffed2f487d8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 [ 112.176009] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f2055bd5620 [ 112.176009] RDX: 000000000000000d RSI: 0000000000ee0008 RDI: 0000000000000001 [ 112.176009] RBP: 0000000000000001 R08: 00007f2055e94760 R09: 00007f20564c7b40 [ 112.176009] R10: 0000000000000073 R11: 0000000000000246 R12: 0000000000000000 [ 112.176009] R13: 00007ffed2f48d70 R14: 0000000000000000 R15: 0000000000000000 [ 112.176009] Code: 00 00 00 55 be 02 00 00 00 48 89 e5 e8 62 fb ff ff 5d c3 55 48 89 e5 53 48 89 fb e8 53 e9 ff ff 65 48 8b 14 25 40 c4 00 00 31 c0 48 0f b1 13 48 85 c0 74 08 48 89 df e8c6 ff ff ff 5b 5d c3 [ 112.176009] RIP: mutex_lock+0x18/0x30 RSP: ffffc90000b5fc78 [ 112.176009] CR2: 0000000000000260 [ 112.205622] ---[ end trace 76189cd7a9bdd155 ]--- Signed-off-by: Gabriel Krisman Bertazi Link: http://patchwork.freedesktop.org/patch/msgid/20170317181409.4183-1-krisman@collabora.co.uk Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/bochs/bochs_fbdev.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c index a11debaad626e..471bd588550bc 100644 --- a/drivers/gpu/drm/bochs/bochs_fbdev.c +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c @@ -132,6 +132,7 @@ static int bochsfb_create(struct drm_fb_helper *helper, info->fix.smem_start = 0; info->fix.smem_len = size; + bochs->fb.initialized = true; return 0; } @@ -148,7 +149,6 @@ static int bochs_fbdev_destroy(struct bochs_device *bochs) gfb->obj = NULL; } - drm_fb_helper_fini(&bochs->fb.helper); drm_framebuffer_unregister_private(&gfb->base); drm_framebuffer_cleanup(&gfb->base); @@ -180,7 +180,6 @@ int bochs_fbdev_init(struct bochs_device *bochs) if (ret) goto fini; - bochs->fb.initialized = true; return 0; fini: @@ -190,9 +189,9 @@ int bochs_fbdev_init(struct bochs_device *bochs) void bochs_fbdev_fini(struct bochs_device *bochs) { - if (!bochs->fb.initialized) - return; + if (bochs->fb.initialized) + bochs_fbdev_destroy(bochs); - bochs_fbdev_destroy(bochs); + drm_fb_helper_fini(&bochs->fb.helper); bochs->fb.initialized = false; } From 3a270e4dcc8a0ee808fd1f2af52d45e8bcfeb9a6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Mar 2017 10:39:25 +0100 Subject: [PATCH 31/37] drm/msm: add stubs for msm_{perf,rd}_debugfs_cleanup We now call those two functions even when they are not defined or declared anywhere because DEBUG_FS is disabled: drivers/gpu/drm/msm/msm_drv.c: In function 'msm_drm_uninit': drivers/gpu/drm/msm/msm_drv.c:244:2: error: implicit declaration of function 'msm_perf_debugfs_cleanup';did you mean 'msm_framebuffer_cleanup'? [-Werror=implicit-function-declaration] drivers/gpu/drm/msm/msm_drv.c:245:2: error: implicit declaration of function 'msm_rd_debugfs_cleanup';did you mean 'msm_framebuffer_cleanup'? [-Werror=implicit-function-declaration] This adds empty stub implementations for that case. Fixes: 85eac4700ede ("drm/msm: Remove msm_debugfs_cleanup()") Signed-off-by: Arnd Bergmann Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170320093936.1255573-1-arnd@arndb.de --- drivers/gpu/drm/msm/msm_drv.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index b51fb0d70f431..b885c3d5ae4df 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -311,6 +311,8 @@ void msm_perf_debugfs_cleanup(struct msm_drm_private *priv); #else static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; } static inline void msm_rd_dump_submit(struct msm_gem_submit *submit) {} +static inline void msm_rd_debugfs_cleanup(struct msm_drm_private *priv) {} +static inline void msm_perf_debugfs_cleanup(struct msm_drm_private *priv) {} #endif struct clk *msm_clk_get(struct platform_device *pdev, const char *name); From 94bb4dc132ed2e3a4d16649b0096c49d13670fe8 Mon Sep 17 00:00:00 2001 From: Nickey Yang Date: Mon, 20 Mar 2017 10:57:31 +0800 Subject: [PATCH 32/37] drm/bridge: dw_hdmi: support i2c extended read mode "I2C Master Interface Extended Read Mode" implements a segment pointer-based read operation using the Special Register configuration. This patch fix https://patchwork.kernel.org/patch/7098101/ mentioned "The current implementation does not support "I2C Master Interface Extended Read Mode" to read data addressed by non-zero segment pointer, this means that if EDID has more than 1 extension blocks, EDID reading operation won't succeed" With this patch, dw-hdmi can read EDID data with 1/2/4 blocks. Signed-off-by: Nickey Yang Reviewed-by: Douglas Anderson Acked-by: Vladimir Zapolskiy Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1489978651-16647-1-git-send-email-nickey.yang@rock-chips.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 38 ++++++++++++++--------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 026a0dce76618..0d112cf6b969c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -33,6 +33,7 @@ #include "dw-hdmi.h" #include "dw-hdmi-audio.h" +#define DDC_SEGMENT_ADDR 0x30 #define HDMI_EDID_LEN 512 #define RGB 0 @@ -112,6 +113,7 @@ struct dw_hdmi_i2c { u8 slave_reg; bool is_regaddr; + bool is_segment; }; struct dw_hdmi_phy_data { @@ -247,8 +249,12 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, reinit_completion(&i2c->cmp); hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS); - hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, - HDMI_I2CM_OPERATION); + if (i2c->is_segment) + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT, + HDMI_I2CM_OPERATION); + else + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, + HDMI_I2CM_OPERATION); stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); if (!stat) @@ -260,6 +266,7 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI); } + i2c->is_segment = false; return 0; } @@ -309,12 +316,6 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr); for (i = 0; i < num; i++) { - if (msgs[i].addr != addr) { - dev_warn(hdmi->dev, - "unsupported transfer, changed slave address\n"); - return -EOPNOTSUPP; - } - if (msgs[i].len == 0) { dev_dbg(hdmi->dev, "unsupported transfer %d/%d, no data\n", @@ -334,15 +335,24 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, /* Set slave device register address on transfer */ i2c->is_regaddr = false; + /* Set segment pointer for I2C extended read mode operation */ + i2c->is_segment = false; + for (i = 0; i < num; i++) { dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n", i + 1, num, msgs[i].len, msgs[i].flags); - - if (msgs[i].flags & I2C_M_RD) - ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, msgs[i].len); - else - ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, msgs[i].len); - + if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { + i2c->is_segment = true; + hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR); + hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR); + } else { + if (msgs[i].flags & I2C_M_RD) + ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, + msgs[i].len); + else + ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, + msgs[i].len); + } if (ret < 0) break; } From 9aa1eca095579b8a8ea84d9bbd1fbdeff49cebd4 Mon Sep 17 00:00:00 2001 From: Nickey Yang Date: Tue, 21 Mar 2017 15:36:17 +0800 Subject: [PATCH 33/37] drm: bridge: dw-hdmi: add HDMI vendor specific infoframe config Vendor specific infoframe is mandatory for 4K2K resolution. Without this, the HDMI protocol compliance fails. Signed-off-by: Nickey Yang Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1490081777-2232-1-git-send-email-nickey.yang@rock-chips.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 53 +++++++++++++++++++++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 4 ++ 2 files changed, 57 insertions(+) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 0d112cf6b969c..af93f7a20697a 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -1240,6 +1240,58 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1); } +static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi, + struct drm_display_mode *mode) +{ + struct hdmi_vendor_infoframe frame; + u8 buffer[10]; + ssize_t err; + + err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode); + if (err < 0) + /* + * Going into that statement does not means vendor infoframe + * fails. It just informed us that vendor infoframe is not + * needed for the selected mode. Only 4k or stereoscopic 3D + * mode requires vendor infoframe. So just simply return. + */ + return; + + err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n", + err); + return; + } + hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET, + HDMI_FC_DATAUTO0_VSD_MASK); + + /* Set the length of HDMI vendor specific InfoFrame payload */ + hdmi_writeb(hdmi, buffer[2], HDMI_FC_VSDSIZE); + + /* Set 24bit IEEE Registration Identifier */ + hdmi_writeb(hdmi, buffer[4], HDMI_FC_VSDIEEEID0); + hdmi_writeb(hdmi, buffer[5], HDMI_FC_VSDIEEEID1); + hdmi_writeb(hdmi, buffer[6], HDMI_FC_VSDIEEEID2); + + /* Set HDMI_Video_Format and HDMI_VIC/3D_Structure */ + hdmi_writeb(hdmi, buffer[7], HDMI_FC_VSDPAYLOAD0); + hdmi_writeb(hdmi, buffer[8], HDMI_FC_VSDPAYLOAD1); + + if (frame.s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) + hdmi_writeb(hdmi, buffer[9], HDMI_FC_VSDPAYLOAD2); + + /* Packet frame interpolation */ + hdmi_writeb(hdmi, 1, HDMI_FC_DATAUTO1); + + /* Auto packets per frame and line spacing */ + hdmi_writeb(hdmi, 0x11, HDMI_FC_DATAUTO2); + + /* Configures the Frame Composer On RDRB mode */ + hdmi_mask_writeb(hdmi, 1, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET, + HDMI_FC_DATAUTO0_VSD_MASK); +} + static void hdmi_av_composer(struct dw_hdmi *hdmi, const struct drm_display_mode *mode) { @@ -1489,6 +1541,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) /* HDMI Initialization Step F - Configure AVI InfoFrame */ hdmi_config_AVI(hdmi, mode); + hdmi_config_vendor_specific_infoframe(hdmi, mode); } else { dev_dbg(hdmi->dev, "%s DVI mode\n", __func__); } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index 325b0b8ae639c..c59f87e1483ea 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h @@ -854,6 +854,10 @@ enum { HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10, HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, +/* FC_DATAUTO0 field values */ + HDMI_FC_DATAUTO0_VSD_MASK = 0x08, + HDMI_FC_DATAUTO0_VSD_OFFSET = 3, + /* PHY_CONF0 field values */ HDMI_PHY_CONF0_PDZ_MASK = 0x80, HDMI_PHY_CONF0_PDZ_OFFSET = 7, From 3ad33ae2bc800496e979b9f7920a57835740083d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 13 Mar 2017 16:53:59 +0530 Subject: [PATCH 34/37] drm: Add SCDC helpers SCDC is a mechanism defined in the HDMI 2.0 specification that allows the source and sink devices to communicate. This commit introduces helpers to access the SCDC and provides the symbolic names for the various registers defined in the specification. V2: Rebase. V3: Added R-B from Jose. V4: Rebase V5: Addressed review comments from Ville - Handle the I2c return values in a better way (dp_dual_mode) - Make the macros for SCDC Major/Minor more readable, by adding a 'GET' in the macro names V6: Rebase V7: Rebase V8: Rebase V9: Rebase V10: Rebase Signed-off-by: Thierry Reding Signed-off-by: Shashank Sharma Reviewed-by: Jose Abreu Signed-off-by: Jani Nikula Link: http://patchwork.freedesktop.org/patch/msgid/1489404244-16608-2-git-send-email-shashank.sharma@intel.com --- Documentation/gpu/drm-kms-helpers.rst | 12 +++ drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_scdc_helper.c | 123 ++++++++++++++++++++++++ include/drm/drm_scdc_helper.h | 132 ++++++++++++++++++++++++++ 4 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_scdc_helper.c create mode 100644 include/drm/drm_scdc_helper.h diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index ac53c0b893f68..c075aadd7078c 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -223,6 +223,18 @@ EDID Helper Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_edid.c :export: +SCDC Helper Functions Reference +=============================== + +.. kernel-doc:: drivers/gpu/drm/drm_scdc_helper.c + :doc: scdc helpers + +.. kernel-doc:: include/drm/drm_scdc_helper.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_scdc_helper.c + :export: + Rectangle Utilities Reference ============================= diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 59aae43005eef..59f0f9b696eb8 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -31,7 +31,8 @@ drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \ drm_kms_helper_common.o drm_dp_dual_mode_helper.o \ - drm_simple_kms_helper.o drm_modeset_helper.o + drm_simple_kms_helper.o drm_modeset_helper.o \ + drm_scdc_helper.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o diff --git a/drivers/gpu/drm/drm_scdc_helper.c b/drivers/gpu/drm/drm_scdc_helper.c new file mode 100644 index 0000000000000..c2dd33f89c17f --- /dev/null +++ b/drivers/gpu/drm/drm_scdc_helper.c @@ -0,0 +1,123 @@ +/* + * 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 + +#include + +/** + * 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); diff --git a/include/drm/drm_scdc_helper.h b/include/drm/drm_scdc_helper.h new file mode 100644 index 0000000000000..9c52deb13df49 --- /dev/null +++ b/include/drm/drm_scdc_helper.h @@ -0,0 +1,132 @@ +/* + * 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 +#include + +#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)); +} + +#endif From 50dd1bd1e2395a78e5adfad96487092e215483e0 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 13 Mar 2017 16:54:00 +0530 Subject: [PATCH 35/37] drm/edid: check for HF-VSDB block This patch implements a small function that finds if a given CEA db is hdmi-forum vendor specific data block or not. V2: Rebase. V3: Added R-B from Jose. V4: Rebase V5: Rebase V6: Rebase V7: Rebase V8: Rebase V9: Rebase V10: Rebase Signed-off-by: Thierry Reding Signed-off-by: Shashank Sharma Reviewed-by: Jose Abreu Signed-off-by: Jani Nikula Link: http://patchwork.freedesktop.org/patch/msgid/1489404244-16608-3-git-send-email-shashank.sharma@intel.com --- drivers/gpu/drm/drm_edid.c | 15 +++++++++++++++ include/linux/hdmi.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 171d7a02ace0d..92e9551f3c381 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3248,6 +3248,21 @@ static bool cea_db_is_hdmi_vsdb(const u8 *db) return hdmi_id == HDMI_IEEE_OUI; } +static bool cea_db_is_hdmi_forum_vsdb(const u8 *db) +{ + unsigned int oui; + + if (cea_db_tag(db) != VENDOR_BLOCK) + return false; + + if (cea_db_payload_len(db) < 7) + return false; + + oui = db[3] << 16 | db[2] << 8 | db[1]; + + return oui == HDMI_FORUM_IEEE_OUI; +} + #define for_each_cea_db(cea, i, start, end) \ for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index edbb4fc674ed6..d271ff23984f4 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -35,6 +35,7 @@ enum hdmi_infoframe_type { }; #define HDMI_IEEE_OUI 0x000c03 +#define HDMI_FORUM_IEEE_OUI 0xc45dd8 #define HDMI_INFOFRAME_HEADER_SIZE 4 #define HDMI_AVI_INFOFRAME_SIZE 13 #define HDMI_SPD_INFOFRAME_SIZE 25 From afa1c763653e0fd68f2d1e3dca869c9453d8ef06 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 13 Mar 2017 16:54:01 +0530 Subject: [PATCH 36/37] drm/edid: detect SCDC support in HF-VSDB This patch does following: - Adds a new structure (drm_hdmi_info) in drm_display_info. This structure will be used to save and indicate if sink supports advanced HDMI 2.0 features - Adds another structure drm_scdc within drm_hdmi_info, to reflect scdc support and capabilities in connected HDMI 2.0 sink. - Checks the HF-VSDB block for presence of SCDC, and marks it in scdc structure - If SCDC is present, checks if sink is capable of generating SCDC read request, and marks it in scdc structure. V2: Addressed review comments Thierry: - Fix typos in commit message and make abbreviation consistent across the commit message. - Change structure object name from hdmi_info -> hdmi - Fix typos and abbreviations in description of structure drm_hdmi_info end the description with a full stop. - Create a structure drm_scdc, and keep all information related to SCDC register set (supported, read request supported) etc in it. Ville: - Change rr -> read_request - Call drm_detect_scrambling function drm_parse_hf_vsdb so that all of HF-VSDB parsing can be kept in same function, in incremental patches. V3: Rebase. V4: Rebase. V5: Rebase. V6: Rebase. V7: Added R-B from Jose. V8: Rebase. V9: Rebase. V10: Rebase. Signed-off-by: Shashank Sharma Reviewed-by: Thierry Reding Reviewed-by: Jose Abreu Signed-off-by: Jani Nikula Link: http://patchwork.freedesktop.org/patch/msgid/1489404244-16608-4-git-send-email-shashank.sharma@intel.com --- drivers/gpu/drm/drm_edid.c | 14 ++++++++++++++ include/drm/drm_connector.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 92e9551f3c381..d6f16c02afb5c 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3814,6 +3814,18 @@ drm_default_rgb_quant_range(const struct drm_display_mode *mode) } EXPORT_SYMBOL(drm_default_rgb_quant_range); +static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector, + const u8 *hf_vsdb) +{ + struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; + + if (hf_vsdb[6] & 0x80) { + hdmi->scdc.supported = true; + if (hf_vsdb[6] & 0x40) + hdmi->scdc.read_request = true; + } +} + static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, const u8 *hdmi) { @@ -3928,6 +3940,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector, if (cea_db_is_hdmi_vsdb(db)) drm_parse_hdmi_vsdb_video(connector, db); + if (cea_db_is_hdmi_forum_vsdb(db)) + drm_parse_hdmi_forum_vsdb(connector, db); } } diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index fabb35aba5f6f..bf9d6f54ad166 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -87,6 +87,34 @@ enum subpixel_order { SubPixelVerticalRGB, SubPixelVerticalBGR, SubPixelNone, + +}; + +/* + * 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; +}; + +/** + * 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 +232,11 @@ struct drm_display_info { * @cea_rev: CEA revision of the HDMI sink. */ u8 cea_rev; + + /** + * @hdmi: advance features of a HDMI sink. + */ + struct drm_hdmi_info hdmi; }; int drm_display_info_set_bus_formats(struct drm_display_info *info, From 62c58af32c935a98a1e8d8ceb39a3a47b36fbbcd Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 13 Mar 2017 16:54:02 +0530 Subject: [PATCH 37/37] drm/edid: detect SCDC support in HF-VSDB This patch does following: - Adds a new structure (drm_hdmi_info) in drm_display_info. This structure will be used to save and indicate if sink supports advanced HDMI 2.0 features - Adds another structure drm_scdc within drm_hdmi_info, to reflect scdc support and capabilities in connected HDMI 2.0 sink. - Checks the HF-VSDB block for presence of SCDC, and marks it in scdc structure - If SCDC is present, checks if sink is capable of generating SCDC read request, and marks it in scdc structure. V2: Addressed review comments Thierry: - Fix typos in commit message and make abbreviation consistent across the commit message. - Change structure object name from hdmi_info -> hdmi - Fix typos and abbreviations in description of structure drm_hdmi_info end the description with a full stop. - Create a structure drm_scdc, and keep all information related to SCDC register set (supported, read request supported) etc in it. Ville: - Change rr -> read_request - Call drm_detect_scrambling function drm_parse_hf_vsdb so that all of HF-VSDB parsing can be kept in same function, in incremental patches. V3: Rebase. V4: Rebase. V5: Rebase. V6: Addressed review comments from Ville - Add clock rate calculations for 1/10 and 1/40 ratios - Remove leftovers from old patchset V7: Added R-B from Jose. V8: Rebase. V9: Rebase. V10: Rebase. Signed-off-by: Shashank Sharma Reviewed-by: Thierry Reding Reviewed-by: Jose Abreu Signed-off-by: Jani Nikula Link: http://patchwork.freedesktop.org/patch/msgid/1489404244-16608-5-git-send-email-shashank.sharma@intel.com --- drivers/gpu/drm/drm_edid.c | 33 +++++++- drivers/gpu/drm/drm_scdc_helper.c | 121 ++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 19 +++++ include/drm/drm_edid.h | 1 - include/drm/drm_scdc_helper.h | 27 +++++++ 5 files changed, 199 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d6f16c02afb5c..99144f879a4f2 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "drm_crtc_internal.h" @@ -3817,13 +3818,43 @@ EXPORT_SYMBOL(drm_default_rgb_quant_range); static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector, const u8 *hf_vsdb) { - struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; + 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, diff --git a/drivers/gpu/drm/drm_scdc_helper.c b/drivers/gpu/drm/drm_scdc_helper.c index c2dd33f89c17f..3cd96a95736d7 100644 --- a/drivers/gpu/drm/drm_scdc_helper.c +++ b/drivers/gpu/drm/drm_scdc_helper.c @@ -22,8 +22,10 @@ */ #include +#include #include +#include /** * DOC: scdc helpers @@ -121,3 +123,122 @@ ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, 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); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index bf9d6f54ad166..f8b766d70a463 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -90,6 +90,20 @@ enum subpixel_order { }; +/** + * 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 * @@ -105,8 +119,13 @@ struct drm_scdc { * @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 * diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 3ead84d93792d..7b9f48b62e07c 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -476,5 +476,4 @@ void drm_edid_get_monitor_name(struct edid *edid, char *name, struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh, bool rb); - #endif /* __DRM_EDID_H__ */ diff --git a/include/drm/drm_scdc_helper.h b/include/drm/drm_scdc_helper.h index 9c52deb13df49..ab6bcfbceba98 100644 --- a/include/drm/drm_scdc_helper.h +++ b/include/drm/drm_scdc_helper.h @@ -129,4 +129,31 @@ static inline int drm_scdc_writeb(struct i2c_adapter *adapter, u8 offset, 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