diff options
125 files changed, 2935 insertions, 1640 deletions
diff --git a/Documentation/.gitignore b/Documentation/.gitignore new file mode 100644 index 000000000000..53752db253e3 --- /dev/null +++ b/Documentation/.gitignore @@ -0,0 +1 @@ +output diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index d70f9b68174e..e0c7e1e0590b 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -33,10 +33,6 @@ PDF_METHOD = $(prefer-db2x) PS_METHOD = $(prefer-db2x) -### -# The targets that may be used. -PHONY += xmldocs sgmldocs psdocs pdfdocs htmldocs mandocs installmandocs cleandocs - targets += $(DOCBOOKS) BOOKS := $(addprefix $(obj)/,$(DOCBOOKS)) xmldocs: $(BOOKS) @@ -63,6 +59,9 @@ installmandocs: mandocs sort -k 2 -k 1 | uniq -f 1 | sed -e 's: :/:' | \ xargs install -m 644 -t /usr/local/man/man9/ +# no-op for the DocBook toolchain +epubdocs: + ### #External programs used KERNELDOCXMLREF = $(srctree)/scripts/kernel-doc-xml-ref diff --git a/Documentation/DocBook/gpu.tmpl b/Documentation/DocBook/gpu.tmpl index dac18b4ff090..d09536c91717 100644 --- a/Documentation/DocBook/gpu.tmpl +++ b/Documentation/DocBook/gpu.tmpl @@ -1018,6 +1018,11 @@ int max_width, max_height;</synopsis> </para> </sect2> <sect2> + <title>DRM Format Handling</title> +!Iinclude/drm/drm_fourcc.h +!Edrivers/gpu/drm/drm_fourcc.c + </sect2> + <sect2> <title>Dumb Buffer Objects</title> <para> The KMS API doesn't standardize backing storage object creation and @@ -1683,6 +1688,12 @@ void intel_crt_init(struct drm_device *dev) !Edrivers/gpu/drm/drm_panel.c !Pdrivers/gpu/drm/drm_panel.c drm panel </sect2> + <sect2> + <title>Simple KMS Helper Reference</title> +!Iinclude/drm/drm_simple_kms_helper.h +!Edrivers/gpu/drm/drm_simple_kms_helper.c +!Pdrivers/gpu/drm/drm_simple_kms_helper.c overview + </sect2> </sect1> <!-- Internals: kms properties --> diff --git a/Documentation/Makefile.sphinx b/Documentation/Makefile.sphinx new file mode 100644 index 000000000000..addf32309bc3 --- /dev/null +++ b/Documentation/Makefile.sphinx @@ -0,0 +1,63 @@ +# -*- makefile -*- +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXBUILD = sphinx-build +SPHINXOPTS = +PAPER = +BUILDDIR = $(obj)/output + +# User-friendly check for sphinx-build +HAVE_SPHINX := $(shell if which $(SPHINXBUILD) >/dev/null 2>&1; then echo 1; else echo 0; fi) + +ifeq ($(HAVE_SPHINX),0) + +.DEFAULT: + $(warning The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed and in PATH, or set the SPHINXBUILD make variable to point to the full path of the '$(SPHINXBUILD)' executable.) + @echo " SKIP Sphinx $@ target." + +else # HAVE_SPHINX + +# User-friendly check for rst2pdf +HAVE_RST2PDF := $(shell if python -c "import rst2pdf" >/dev/null 2>&1; then echo 1; else echo 0; fi) + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +KERNELDOC = $(srctree)/scripts/kernel-doc +KERNELDOC_CONF = -D kerneldoc_srctree=$(srctree) -D kerneldoc_bin=$(KERNELDOC) +ALLSPHINXOPTS = -D version=$(KERNELVERSION) -D release=$(KERNELRELEASE) -d $(BUILDDIR)/.doctrees $(KERNELDOC_CONF) $(PAPEROPT_$(PAPER)) -c $(srctree)/$(src) $(SPHINXOPTS) $(srctree)/$(src) +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +quiet_cmd_sphinx = SPHINX $@ + cmd_sphinx = $(SPHINXBUILD) -b $2 $(ALLSPHINXOPTS) $(BUILDDIR)/$2 + +htmldocs: + $(call cmd,sphinx,html) + +pdfdocs: +ifeq ($(HAVE_RST2PDF),0) + $(warning The Python 'rst2pdf' module was not found. Make sure you have the module installed to produce PDF output.) + @echo " SKIP Sphinx $@ target." +else # HAVE_RST2PDF + $(call cmd,sphinx,pdf) +endif # HAVE_RST2PDF + +epubdocs: + $(call cmd,sphinx,epub) + +xmldocs: + $(call cmd,sphinx,xml) + +# no-ops for the Sphinx toolchain +sgmldocs: +psdocs: +mandocs: +installmandocs: + +cleandocs: + $(Q)rm -rf $(BUILDDIR) + +endif # HAVE_SPHINX diff --git a/Documentation/conf.py b/Documentation/conf.py new file mode 100644 index 000000000000..6cc41a0555a3 --- /dev/null +++ b/Documentation/conf.py @@ -0,0 +1,414 @@ +# -*- coding: utf-8 -*- +# +# The Linux Kernel documentation build configuration file, created by +# sphinx-quickstart on Fri Feb 12 13:51:46 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('sphinx')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['kernel-doc'] + +# Gracefully handle missing rst2pdf. +try: + import rst2pdf + extensions += ['rst2pdf.pdfbuilder'] +except ImportError: + pass + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'The Linux Kernel' +copyright = '2016, The kernel development community' +author = 'The kernel development community' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# In a normal build, version and release are are set to KERNELVERSION and +# KERNELRELEASE, respectively, from the Makefile via Sphinx command line +# arguments. +# +# The following code tries to extract the information by reading the Makefile, +# when Sphinx is run directly (e.g. by Read the Docs). +try: + makefile_version = None + makefile_patchlevel = None + for line in open('../Makefile'): + key, val = [x.strip() for x in line.split('=', 2)] + if key == 'VERSION': + makefile_version = val + elif key == 'PATCHLEVEL': + makefile_patchlevel = val + if makefile_version and makefile_patchlevel: + break +except: + pass +finally: + if makefile_version and makefile_patchlevel: + version = release = makefile_version + '.' + makefile_patchlevel + else: + sys.stderr.write('Warning: Could not extract kernel version\n') + version = release = "unknown version" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['output'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + +primary_domain = 'C' +highlight_language = 'C' + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. + +# The Read the Docs theme is available from +# - https://github.com/snide/sphinx_rtd_theme +# - https://pypi.python.org/pypi/sphinx_rtd_theme +# - python-sphinx-rtd-theme package (on Debian) +try: + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +except ImportError: + sys.stderr.write('Warning: The Sphinx \'sphinx_rtd_theme\' HTML theme was not found. Make sure you have the theme installed to produce pretty HTML output. Falling back to the default theme.\n') + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'TheLinuxKerneldoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'TheLinuxKernel.tex', 'The Linux Kernel Documentation', + 'The kernel development community', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'thelinuxkernel', 'The Linux Kernel Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'TheLinuxKernel', 'The Linux Kernel Documentation', + author, 'TheLinuxKernel', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright + +# The basename for the epub file. It defaults to the project name. +#epub_basename = project + +# The HTML theme for the epub output. Since the default themes are not +# optimized for small screen space, using the same theme for HTML and epub +# output is usually not wise. This defaults to 'epub', a theme designed to save +# visual space. +#epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or 'en' if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +#epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +#epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files that should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +#epub_tocscope = 'default' + +# Fix unsupported image types using the Pillow. +#epub_fix_images = False + +# Scale large images. +#epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#epub_show_urls = 'inline' + +# If false, no index is generated. +#epub_use_index = True + +#======= +# rst2pdf +# +# Grouping the document tree into PDF files. List of tuples +# (source start file, target name, title, author, options). +# +# See the Sphinx chapter of http://ralsina.me/static/manual.pdf +# +# FIXME: Do not add the index file here; the result will be too big. Adding +# multiple PDF files here actually tries to get the cross-referencing right +# *between* PDF files. +pdf_documents = [ + ('index', u'Kernel', u'Kernel', u'J. Random Bozo'), +] + +# kernel-doc extension configuration for running Sphinx directly (e.g. by Read +# the Docs). In a normal build, these are supplied from the Makefile via command +# line arguments. +kerneldoc_bin = '../scripts/kernel-doc' +kerneldoc_srctree = '..' diff --git a/Documentation/dmaengine/provider.txt b/Documentation/dmaengine/provider.txt index 122b7f4876bb..91ce82d5f0c4 100644 --- a/Documentation/dmaengine/provider.txt +++ b/Documentation/dmaengine/provider.txt @@ -323,7 +323,7 @@ supported. * device_resume - Resumes a transfer on the channel - This command should operate synchronously on the channel, - pausing right away the work of the given channel + resuming right away the work of the given channel * device_terminate_all - Aborts all the pending and ongoing transfers on the channel diff --git a/Documentation/index.rst b/Documentation/index.rst new file mode 100644 index 000000000000..71a276f34c7f --- /dev/null +++ b/Documentation/index.rst @@ -0,0 +1,23 @@ +.. The Linux Kernel documentation master file, created by + sphinx-quickstart on Fri Feb 12 13:51:46 2016. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to The Linux Kernel's documentation! +============================================ + +Nothing for you to see here *yet*. Please move along. + +Contents: + +.. toctree:: + :maxdepth: 2 + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 82b42c958d1c..a2a662d4da83 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -3992,8 +3992,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted. trace_event=[event-list] [FTRACE] Set and start specified trace events in order - to facilitate early boot debugging. - See also Documentation/trace/events.txt + to facilitate early boot debugging. The event-list is a + comma separated list of trace events to enable. See + also Documentation/trace/events.txt trace_options=[option-list] [FTRACE] Enable or disable tracer options at boot. diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c index 30fb842a976d..49db1def1721 100644 --- a/Documentation/mic/mpssd/mpssd.c +++ b/Documentation/mic/mpssd/mpssd.c @@ -1538,9 +1538,9 @@ set_cmdline(struct mic_info *mic) len = snprintf(buffer, PATH_MAX, "clocksource=tsc highres=off nohz=off "); - len += snprintf(buffer + len, PATH_MAX, + len += snprintf(buffer + len, PATH_MAX - len, "cpufreq_on;corec6_off;pc3_off;pc6_off "); - len += snprintf(buffer + len, PATH_MAX, + len += snprintf(buffer + len, PATH_MAX - len, "ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0", mic->id + 1); diff --git a/Documentation/security/self-protection.txt b/Documentation/security/self-protection.txt index babd6378ec05..3010576c9fca 100644 --- a/Documentation/security/self-protection.txt +++ b/Documentation/security/self-protection.txt @@ -183,8 +183,9 @@ provide meaningful defenses. ### Canaries, blinding, and other secrets It should be noted that things like the stack canary discussed earlier -are technically statistical defenses, since they rely on a (leakable) -secret value. +are technically statistical defenses, since they rely on a secret value, +and such values may become discoverable through an information exposure +flaw. Blinding literal values for things like JITs, where the executable contents may be partially under the control of userspace, need a similar @@ -199,8 +200,8 @@ working?) in order to maximize their success. Since the location of kernel memory is almost always instrumental in mounting a successful attack, making the location non-deterministic raises the difficulty of an exploit. (Note that this in turn makes -the value of leaks higher, since they may be used to discover desired -memory locations.) +the value of information exposures higher, since they may be used to +discover desired memory locations.) #### Text and module base @@ -222,14 +223,21 @@ become more difficult to locate. Much of the kernel's dynamic memory (e.g. kmalloc, vmalloc, etc) ends up being relatively deterministic in layout due to the order of early-boot initializations. If the base address of these areas is not the same -between boots, targeting them is frustrated, requiring a leak specific -to the region. +between boots, targeting them is frustrated, requiring an information +exposure specific to the region. + +#### Structure layout + +By performing a per-build randomization of the layout of sensitive +structures, attacks must either be tuned to known kernel builds or expose +enough kernel memory to determine structure layouts before manipulating +them. -## Preventing Leaks +## Preventing Information Exposures Since the locations of sensitive structures are the primary target for -attacks, it is important to defend against leaks of both kernel memory +attacks, it is important to defend against exposure of both kernel memory addresses and kernel memory contents (since they may contain kernel addresses or other sensitive things like canary values). @@ -250,8 +258,8 @@ sure structure holes are cleared. When releasing memory, it is best to poison the contents (clear stack on syscall return, wipe heap memory on a free), to avoid reuse attacks that rely on the old contents of memory. This frustrates many uninitialized -variable attacks, stack info leaks, heap info leaks, and use-after-free -attacks. +variable attacks, stack content exposures, heap content exposures, and +use-after-free attacks. ### Destination tracking diff --git a/Documentation/sphinx/convert_template.sed b/Documentation/sphinx/convert_template.sed new file mode 100644 index 000000000000..c1503fcca4ec --- /dev/null +++ b/Documentation/sphinx/convert_template.sed @@ -0,0 +1,18 @@ +# +# Pandoc doesn't grok <function> or <structname>, so convert them +# ahead of time. +# +# Use the following escapes to pass through pandoc: +# $bq = "`" +# $lt = "<" +# $gt = ">" +# +s%<function>\([^<(]\+\)()</function>%:c:func:$bq\1()$bq%g +s%<function>\([^<(]\+\)</function>%:c:func:$bq\1()$bq%g +s%<structname>struct *\([^<]\+\)</structname>%:c:type:$bqstruct \1 $lt\1$gt$bq%g +s%struct <structname>\([^<]\+\)</structname>%:c:type:$bqstruct \1 $lt\1$gt$bq%g +s%<structname>\([^<]\+\)</structname>%:c:type:$bqstruct \1 $lt\1$gt$bq%g +# +# Wrap docproc directives in para and code blocks. +# +s%^\(!.*\)$%<para><code>DOCPROC: \1</code></para>% diff --git a/Documentation/sphinx/kernel-doc.py b/Documentation/sphinx/kernel-doc.py new file mode 100644 index 000000000000..4adfb0e91ecc --- /dev/null +++ b/Documentation/sphinx/kernel-doc.py @@ -0,0 +1,127 @@ +# coding=utf-8 +# +# Copyright © 2016 Intel Corporation +# +# 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, sublicense, +# 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 NONINFRINGEMENT. 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. +# +# Authors: +# Jani Nikula <jani.nikula@intel.com> +# +# Please make sure this works on both python2 and python3. +# + +import os +import subprocess +import sys +import re + +from docutils import nodes, statemachine +from docutils.statemachine import ViewList +from docutils.parsers.rst import directives +from sphinx.util.compat import Directive + +class KernelDocDirective(Directive): + """Extract kernel-doc comments from the specified file""" + required_argument = 1 + optional_arguments = 4 + option_spec = { + 'doc': directives.unchanged_required, + 'functions': directives.unchanged_required, + 'export': directives.flag, + 'internal': directives.flag, + } + has_content = False + + def run(self): + env = self.state.document.settings.env + cmd = [env.config.kerneldoc_bin, '-rst', '-enable-lineno'] + + filename = env.config.kerneldoc_srctree + '/' + self.arguments[0] + + # Tell sphinx of the dependency + env.note_dependency(os.path.abspath(filename)) + + tab_width = self.options.get('tab-width', self.state.document.settings.tab_width) + source = filename + + # FIXME: make this nicer and more robust against errors + if 'export' in self.options: + cmd += ['-export'] + elif 'internal' in self.options: + cmd += ['-internal'] + elif 'doc' in self.options: + cmd += ['-function', str(self.options.get('doc'))] + elif 'functions' in self.options: + for f in str(self.options.get('functions')).split(' '): + cmd += ['-function', f] + + cmd += [filename] + + try: + env.app.verbose('calling kernel-doc \'%s\'' % (" ".join(cmd))) + + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + out, err = p.communicate() + + # python2 needs conversion to unicode. + # python3 with universal_newlines=True returns strings. + if sys.version_info.major < 3: + out, err = unicode(out, 'utf-8'), unicode(err, 'utf-8') + + if p.returncode != 0: + sys.stderr.write(err) + + env.app.warn('kernel-doc \'%s\' failed with return code %d' % (" ".join(cmd), p.returncode)) + return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] + elif env.config.kerneldoc_verbosity > 0: + sys.stderr.write(err) + + lines = statemachine.string2lines(out, tab_width, convert_whitespace=True) + result = ViewList() + + lineoffset = 0; + line_regex = re.compile("^#define LINENO ([0-9]+)$") + for line in lines: + match = line_regex.search(line) + if match: + # sphinx counts lines from 0 + lineoffset = int(match.group(1)) - 1 + # we must eat our comments since the upset the markup + else: + result.append(line, source, lineoffset) + lineoffset += 1 + + node = nodes.section() + node.document = self.state.document + self.state.nested_parse(result, self.content_offset, node) + + return node.children + + except Exception as e: + env.app.warn('kernel-doc \'%s\' processing failed with: %s' % + (" ".join(cmd), str(e))) + return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] + +def setup(app): + app.add_config_value('kerneldoc_bin', None, 'env') + app.add_config_value('kerneldoc_srctree', None, 'env') + app.add_config_value('kerneldoc_verbosity', 1, 'env') + + app.add_directive('kernel-doc', KernelDocDirective) diff --git a/Documentation/sphinx/post_convert.sed b/Documentation/sphinx/post_convert.sed new file mode 100644 index 000000000000..392770bac53b --- /dev/null +++ b/Documentation/sphinx/post_convert.sed @@ -0,0 +1,23 @@ +# +# Unescape. +# +s/$bq/`/g +s/$lt/</g +s/$gt/>/g +# +# pandoc thinks that both "_" needs to be escaped. Remove the extra +# backslashes. +# +s/\\_/_/g +# +# Unwrap docproc directives. +# +s/^``DOCPROC: !E\(.*\)``$/.. kernel-doc:: \1\n :export:/ +s/^``DOCPROC: !I\(.*\)``$/.. kernel-doc:: \1\n :internal:/ +s/^``DOCPROC: !F\([^ ]*\) \(.*\)``$/.. kernel-doc:: \1\n :functions: \2/ +s/^``DOCPROC: !P\([^ ]*\) \(.*\)``$/.. kernel-doc:: \1\n :doc: \2/ +s/^``DOCPROC: \(!.*\)``$/.. WARNING: DOCPROC directive not supported: \1/ +# +# Trim trailing whitespace. +# +s/[[:space:]]*$// diff --git a/Documentation/sphinx/tmplcvt b/Documentation/sphinx/tmplcvt new file mode 100755 index 000000000000..909a73065e0a --- /dev/null +++ b/Documentation/sphinx/tmplcvt @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Convert a template file into something like RST +# +# fix <function> +# feed to pandoc +# fix \_ +# title line? +# + +in=$1 +rst=$2 +tmp=$rst.tmp + +cp $in $tmp +sed --in-place -f convert_template.sed $tmp +pandoc -s -S -f docbook -t rst -o $rst $tmp +sed --in-place -f post_convert.sed $rst +rm $tmp diff --git a/Documentation/sync_file.txt b/Documentation/sync_file.txt index eaf8297dbca2..e8e2ebafe5fa 100644 --- a/Documentation/sync_file.txt +++ b/Documentation/sync_file.txt @@ -6,8 +6,8 @@ This document serves as a guide for device drivers writers on what the sync_file API is, and how drivers can support it. Sync file is the carrier of -the fences(struct fence) that needs to synchronized between drivers or across -process boundaries. +the fences(struct fence) that are needed to synchronize between drivers or +across process boundaries. The sync_file API is meant to be used to send and receive fence information to/from userspace. It enables userspace to do explicit fencing, where instead @@ -32,7 +32,7 @@ in-fences and out-fences Sync files can go either to or from userspace. When a sync_file is sent from the driver to userspace we call the fences it contains 'out-fences'. They are related to a buffer that the driver is processing or is going to process, so -the driver an create out-fence to be able to notify, through fence_signal(), +the driver creates an out-fence to be able to notify, through fence_signal(), when it has finished using (or processing) that buffer. Out-fences are fences that the driver creates. diff --git a/Documentation/zh_CN/CodingStyle b/Documentation/zh_CN/CodingStyle index 654afd72eb24..bbb9d6ae05ca 100644 --- a/Documentation/zh_CN/CodingStyle +++ b/Documentation/zh_CN/CodingStyle @@ -24,34 +24,33 @@ Documentation/CodingStyleç„䏿–‡ç¿»è¯‘ Linuxå†…æ ¸ä»£ç 飿 ¼ -这是一个简çŸç„文档,æè¿°äº†linuxå†…æ ¸ç„首选代ç 飿 ¼ă€‚代ç 飿 ¼æ˜¯å› 人而异ç„,而且我 -䏿„¿æ„ææˆ‘ç„观点强å 给任何人,ä¸è¿‡è¿™é‡Œæ‰€è®²è¿°ç„是我必须è¦ç»´æ¤ç„ä»£ç æ‰€éµå®ˆç„飿 ¼ï¼Œ -并且我也希望ç»å¤§å¤æ•°å…¶ä»–代ç 也能éµå®ˆè¿™ä¸ªé£æ ¼ă€‚è¯·åœ¨å†™ä»£ç æ—¶è‡³å°‘è€ƒè™‘ä¸€ä¸‹æœ¬æ–‡æ‰€è¿°ç„ -飿 ¼ă€‚ +这是一个简çŸç„文档,æè¿°äº† linux å†…æ ¸ç„首选代ç 飿 ¼ă€‚代ç 飿 ¼æ˜¯å› 人而异ç„,而且我 +䏿„¿æ„æè‡ªå·±ç„观点强å ç»™ä»»ä½•äººï¼Œä½†è¿™å°±åƒæˆ‘å»å任何事情都必须éµå¾ªç„åŸåˆ™é‚£æ ·ï¼Œæˆ‘也 +希望在ç»å¤§å¤æ•°äº‹ä¸ä¿æŒè¿™ç§ç„æ€åº¦ă€‚è¯·ï¼ˆåœ¨å†™ä»£ç æ—¶ï¼‰è‡³å°‘考虑一下这里ç„代ç 飿 ¼ă€‚ -é¦–å…ˆï¼Œæˆ‘å»ºè®®ä½ æ‰“å°ä¸€ä»½GNU代ç 规范,然åä¸è¦è¯»å®ƒă€‚烧了它,这是一个具有é‡å¤§è±¡å¾æ€§ -æ„义ç„å¨ä½œă€‚ +é¦–å…ˆï¼Œæˆ‘å»ºè®®ä½ æ‰“å°ä¸€ä»½ GNU 代ç 规范,然åä¸è¦è¯»ă€‚烧了它,这是一个具有é‡å¤§è±¡å¾æ€§æ„义 +ç„å¨ä½œă€‚ ä¸ç®¡æ€æ ·ï¼Œç°åœ¨æˆ‘ä»¬å¼€å§‹ï¼ - ç¬¬ä¸€ç« ï¼ç¼©è¿› + ç¬¬ä¸€ç« ï¼ç¼©è¿› -制表符是8个å—符,所以缩进也是8个å—ç¬¦ă€‚æœ‰äº›å¼‚ç«¯è¿å¨è¯•图将缩进å˜ä¸º4(乃至2)个å—符 -æ·±ï¼Œè¿™å‡ ä¹ç›¸å½“äºå°è¯•将圆周ç‡ç„值å®ä¹‰ä¸º3。 +制表符是 8 个å—符,所以缩进也是 8 个å—ç¬¦ă€‚æœ‰äº›å¼‚ç«¯è¿å¨è¯•图将缩进å˜ä¸º 4(ç”至 2ï¼ï¼‰ +个å—ç¬¦æ·±ï¼Œè¿™å‡ ä¹ç›¸å½“äºå°è¯•将圆周ç‡ç„值å®ä¹‰ä¸º 3。 ç†ç”±ï¼ç¼©è¿›ç„全部æ„ä¹‰å°±åœ¨äºæ¸…æ¥ç„å®ä¹‰ä¸€ä¸ªæ§åˆ¶å—èµ·æ¢äºä½•å¤„ă€‚å°¤å…¶æ˜¯å½“ä½ ç›¯ç€ä½ ç„å±å¹• -è¿ç»çœ‹äº†20å°æ—¶ä¹‹åï¼Œä½ å°†ä¼å‘ç°å¤§ä¸€ç‚¹ç„缩进ä¼ä½¿ä½ æ›´å®¹æ˜“åˆ†è¾¨ç¼©è¿›ă€‚ +è¿ç»çœ‹äº† 20 å°æ—¶ä¹‹åï¼Œä½ å°†ä¼å‘ç°å¤§ä¸€ç‚¹ç„缩进ä¼ä½¿ä½ æ›´å®¹æ˜“åˆ†è¾¨ç¼©è¿›ă€‚ -ç°åœ¨ï¼Œæœ‰äº›äººä¼æ±æ€¨8个å—符ç„缩进ä¼ä½¿ä»£ç å‘å³è¾¹ç§»å¨ç„太远,在80个å—符ç„终端å±å¹•ä¸ -就很é¾è¯»è¿™æ ·ç„代ç ă€‚è¿™ä¸ªé—®é¢˜ç„ç”æ¡ˆæ˜¯ï¼Œå¦‚æœä½ 需è¦3级以ä¸ç„缩进,ä¸ç®¡ç”¨ä½•ç§æ–¹å¼ä½ +ç°åœ¨ï¼Œæœ‰äº›äººä¼æ±æ€¨ 8 个å—符ç„缩进ä¼ä½¿ä»£ç å‘å³è¾¹ç§»å¨ç„太远,在 80 个å—符ç„终端å±å¹•ä¸ +就很é¾è¯»è¿™æ ·ç„代ç ă€‚è¿™ä¸ªé—®é¢˜ç„ç”æ¡ˆæ˜¯ï¼Œå¦‚æœä½ éœ€è¦ 3 级以ä¸ç„缩进,ä¸ç®¡ç”¨ä½•ç§æ–¹å¼ä½ ç„代ç å·²ç»æœ‰é—®é¢˜äº†ï¼Œåº”该修æ£ä½ ç„程åºă€‚ -简而言之,8个å—符ç„缩进å¯ä»¥è®©ä»£ç æ›´å®¹æ˜“é˜…è¯»ï¼Œè¿˜æœ‰ä¸€ä¸ªå¥½å¤„æ˜¯å½“ä½ ç„å‡½æ•°åµŒå¥—å¤ªæ·±ç„ +简而言之,8 个å—符ç„缩进å¯ä»¥è®©ä»£ç æ›´å®¹æ˜“é˜…è¯»ï¼Œè¿˜æœ‰ä¸€ä¸ªå¥½å¤„æ˜¯å½“ä½ ç„å‡½æ•°åµŒå¥—å¤ªæ·±ç„ æ—¶å€™å¯ä»¥ç»™ä½ è¦å‘ă€‚ç•™å¿ƒè¿™ä¸ªè¦å‘。 -在switchè¯å¥ä¸æ¶ˆé™¤å¤çº§ç¼©è¿›ç„é¦–é€‰ç„æ–¹å¼æ˜¯è®©â€œswitchâ€å’Œä»å±äºå®ƒç„“caseâ€æ ‡ç¾å¯¹é½äºåŒ -一列,而ä¸è¦â€œä¸¤æ¬¡ç¼©è¿›â€â€œcaseâ€æ ‡ç¾ă€‚æ¯”å¦‚ï¼ +在 switch è¯å¥ä¸æ¶ˆé™¤å¤çº§ç¼©è¿›ç„é¦–é€‰ç„æ–¹å¼æ˜¯è®© “switch†和ä»å±äºå®ƒç„ “caseâ€ æ ‡ç¾ +对é½äºåŒä¸€åˆ—,而ä¸è¦ “两次缩进†“caseâ€ æ ‡ç¾ă€‚æ¯”å¦‚ï¼ switch (suffix) { case 'G': @@ -70,7 +69,6 @@ Documentation/CodingStyleç„䏿–‡ç¿»è¯‘ break; } - ä¸è¦æå¤ä¸ªè¯å¥æ”¾åœ¨ä¸€è¡Œé‡Œï¼Œé™¤éä½ æœ‰ä»€ä¹ˆä¸œè¥¿è¦éè—ï¼ if (condition) do_this; @@ -79,7 +77,7 @@ Documentation/CodingStyleç„䏿–‡ç¿»è¯‘ 也ä¸è¦åœ¨ä¸€è¡Œé‡Œæ”¾å¤ä¸ªèµ‹å€¼è¯å¥ă€‚å†…æ ¸ä»£ç 飿 ¼è¶…级简å•ă€‚å°±æ˜¯é¿å…å¯èƒ½å¯¼è‡´åˆ«äººè¯¯è¯»ç„表 è¾¾å¼ă€‚ -除了注é‡ă€æ–‡æ¡£å’ŒKconfig之外,ä¸è¦ä½¿ç”¨ç©ºæ ¼æ¥ç¼©è¿›ï¼Œå‰é¢ç„ä¾‹åæ˜¯ä¾‹å¤–,是有æ„ä¸ºä¹‹ă€‚ +除了注é‡ă€æ–‡æ¡£å’Œ Kconfig 之外,ä¸è¦ä½¿ç”¨ç©ºæ ¼æ¥ç¼©è¿›ï¼Œå‰é¢ç„ä¾‹åæ˜¯ä¾‹å¤–,是有æ„ä¸ºä¹‹ă€‚ 选用一个好ç„编辑器,ä¸è¦åœ¨è¡Œå°¾ç•™ç©ºæ ¼ă€‚ @@ -88,27 +86,18 @@ Documentation/CodingStyleç„䏿–‡ç¿»è¯‘ 代ç 飿 ¼ç„æ„义就在äºä½¿ç”¨å¹³å¸¸ä½¿ç”¨ç„工具æ¥ç»´æŒä»£ç ç„å¯è¯»æ€§å’Œå¯ç»´æ¤æ€§ă€‚ -æ¯ä¸€è¡Œç„长度ç„é™åˆ¶æ˜¯80列,我们强烈建议您éµå®ˆè¿™ä¸ªæƒ¯ä¾‹ă€‚ +æ¯ä¸€è¡Œç„长度ç„é™åˆ¶æ˜¯ 80 列,我们强烈建议您éµå®ˆè¿™ä¸ªæƒ¯ä¾‹ă€‚ -é•¿äº80列ç„è¯å¥è¦æ‰“æ•£æˆæœ‰æ„义ç„ç‰‡æ®µă€‚æ¯ä¸ªç‰‡æ®µè¦æ˜æ˜¾çŸäºåŸæ¥ç„è¯å¥ï¼Œè€Œä¸”放置ç„ä½ç½® -ä¹Ÿæ˜æ˜¾ç„é å³ă€‚åŒæ ·ç„è§„åˆ™ä¹Ÿé€‚ç”¨äºæœ‰å¾ˆé•¿å‚数列表ç„å‡½æ•°å¤´ă€‚é•¿å—ç¬¦ä¸²ä¹Ÿè¦æ‰“æ•£æˆè¾ƒçŸç„ -å—ç¬¦ä¸²ă€‚å”¯ä¸€ç„例外是超过80列å¯ä»¥å¤§å¹…度æé«˜å¯è¯»æ€§å¹¶ä¸”ä¸ä¼éè—ä¿¡æ¯ç„æƒ…å†µă€‚ - -void fun(int a, int b, int c) -{ - if (condition) - printk(KERN_WARNING "Warning this is a long printk with " - "3 parameters a: %u b: %u " - "c: %u \n", a, b, c); - else - next_statement; -} +é•¿äº 80 列ç„è¯å¥è¦æ‰“æ•£æˆæœ‰æ„义ç„ç‰‡æ®µă€‚é™¤é超过 80 列能显著å¢å å¯è¯»æ€§ï¼Œå¹¶ä¸”ä¸ä¼éè— +ä¿¡æ¯ă€‚åç‰‡æ®µè¦æ˜æ˜¾çŸäºæ¯ç‰‡æ®µï¼Œå¹¶æ˜æ˜¾é å³ă€‚è¿™åŒæ ·é€‚ç”¨äºæœ‰ç€å¾ˆé•¿å‚数列表ç„å‡½æ•°å¤´ă€‚ +然而,ç»å¯¹ä¸è¦æ‰“散对用户å¯è§ç„å—符串,例如 printk ä¿¡æ¯ï¼Œå› ä¸ºè¿™å°†å¯¼è‡´æ— æ³• grep 这些 +ä¿¡æ¯ă€‚ ç¬¬ä¸‰ç« ï¼å¤§æ‹¬å·å’Œç©ºæ ¼ç„放置 Cè¯è¨€é£æ ¼ä¸å¦å¤–一个常è§é—®é¢˜æ˜¯å¤§æ‹¬å·ç„æ”¾ç½®ă€‚å’Œç¼©è¿›å¤§å°ä¸åŒï¼Œé€‰æ‹©æˆ–弃用æŸç§æ”¾ç½®ç– -略并没有å¤å°‘æ€æœ¯ä¸ç„åŸå› ,ä¸è¿‡é¦–é€‰ç„æ–¹å¼ï¼Œå°±åƒKernighanå’ŒRitchie展示给我们ç„,是 -æèµ·å§‹å¤§æ‹¬å·æ”¾åœ¨è¡Œå°¾ï¼Œè€Œæç»“æŸå¤§æ‹¬å·æ”¾åœ¨è¡Œé¦–ï¼Œæ‰€ä»¥ï¼ +略并没有å¤å°‘æ€æœ¯ä¸ç„åŸå› ,ä¸è¿‡é¦–é€‰ç„æ–¹å¼ï¼Œå°±åƒ Kernighan å’Œ Ritchie 展示给我们ç„, +是æèµ·å§‹å¤§æ‹¬å·æ”¾åœ¨è¡Œå°¾ï¼Œè€Œæç»“æŸå¤§æ‹¬å·æ”¾åœ¨è¡Œé¦–ï¼Œæ‰€ä»¥ï¼ if (x is true) { we do y @@ -134,12 +123,12 @@ Cè¯è¨€é£æ ¼ä¸å¦å¤–一个常è§é—®é¢˜æ˜¯å¤§æ‹¬å·ç„æ”¾ç½®ă€‚å’Œç¼©è¿›å¤§å°ä body of function } -全世界ç„异端å¯èƒ½ä¼æ±æ€¨è¿™ä¸ªä¸ä¸€è‡´æ€§æ˜¯â€¦â€¦å‘ƒâ€¦â€¦ä¸ä¸€è‡´ç„,ä¸è¿‡æ‰€æœ‰æ€ç»´å¥å…¨ç„人都知é“( -a)K&R是_æ£ç¡®ç„_,并且(b)K&R是æ£ç¡®ç„。æ¤å¤–,ä¸ç®¡æ€æ ·å‡½æ•°éƒ½æ˜¯ç‰¹æ®ç„(在Cè¯è¨€ä¸ -,函数是ä¸èƒ½åµŒå¥—ç„ï¼‰ă€‚ +全世界ç„异端å¯èƒ½ä¼æ±æ€¨è¿™ä¸ªä¸ä¸€è‡´æ€§æ˜¯â€¦â€¦å‘ƒâ€¦â€¦ä¸ä¸€è‡´ç„,ä¸è¿‡æ‰€æœ‰æ€ç»´å¥å…¨ç„äººéƒ½çŸ¥é“ +(a) K&R 是 _æ£ç¡®ç„_,并且 (b) K&R 是æ£ç¡®ç„。æ¤å¤–,ä¸ç®¡æ€æ ·å‡½æ•°éƒ½æ˜¯ç‰¹æ®ç„(C +函数是ä¸èƒ½åµŒå¥—ç„ï¼‰ă€‚ -注æ„结æŸå¤§æ‹¬å·ç‹¬è‡ªå æ®ä¸€è¡Œï¼Œé™¤é它åé¢è·Ÿç€åŒä¸€ä¸ªè¯å¥ç„剩余部分,也就是doè¯å¥ä¸ç„ -“whileâ€æˆ–者ifè¯å¥ä¸ç„“elseâ€ï¼Œåƒè¿™æ ·ï¼ +注æ„结æŸå¤§æ‹¬å·ç‹¬è‡ªå æ®ä¸€è¡Œï¼Œé™¤é它åé¢è·Ÿç€åŒä¸€ä¸ªè¯å¥ç„剩余部分,也就是 do è¯å¥ä¸ç„ +“while†或者 if è¯å¥ä¸ç„ “elseâ€ï¼Œåƒè¿™æ ·ï¼ do { body of do-loop @@ -158,41 +147,50 @@ a)K&R是_æ£ç¡®ç„_,并且(b)K&R是æ£ç¡®ç„。æ¤å¤–,ä¸ç®¡æ€æ ·å‡½æ ç†ç”±ï¼K&R。 也请注æ„è¿™ç§å¤§æ‹¬å·ç„放置方å¼ä¹Ÿèƒ½ä½¿ç©ºï¼ˆæˆ–者差ä¸å¤ç©ºç„ï¼‰è¡Œç„æ•°é‡æœ€å°åŒ–ï¼ŒåŒæ—¶ä¸å¤±å¯ -è¯»æ€§ă€‚å› æ¤ï¼Œç”±äºä½ ç„å±å¹•ä¸ç„新行是ä¸å¯å†ç”Ÿèµ„æºï¼ˆæƒ³æƒ³25行ç„终端å±å¹•ï¼‰ï¼Œä½ å°†ä¼æœ‰æ›´ +è¯»æ€§ă€‚å› æ¤ï¼Œç”±äºä½ ç„å±å¹•ä¸ç„新行是ä¸å¯å†ç”Ÿèµ„æºï¼ˆæƒ³æƒ³ 25 行ç„终端å±å¹•ï¼‰ï¼Œä½ å°†ä¼æœ‰æ›´ å¤ç„ç©ºè¡Œæ¥æ”¾ç½®æ³¨é‡ă€‚ å½“åªæœ‰ä¸€ä¸ªå•独ç„è¯å¥ç„时候,ä¸ç”¨å ä¸å¿…è¦ç„大括å·ă€‚ -if (condition) - action(); + if (condition) + action(); + +å’Œ + + if (condition) + do_this(); + else + do_that(); -这点ä¸é€‚ç”¨äºæœ¬èº«ä¸ºæŸä¸ªæ¡ä»¶è¯å¥ç„一个分支ç„å•独è¯å¥ă€‚这时需è¦åœ¨ä¸¤ä¸ªåˆ†æ”¯é‡Œéƒ½ä½¿ç”¨å¤§ -括å·ă€‚ +这并ä¸é€‚用äºåªæœ‰ä¸€ä¸ªæ¡ä»¶åˆ†æ”¯æ˜¯å•è¯å¥ç„情况;这时所有分支都è¦ä½¿ç”¨å¤§æ‹¬å·ï¼ -if (condition) { - do_this(); - do_that(); -} else { - otherwise(); -} + if (condition) { + do_this(); + do_that(); + } else { + otherwise(); + } 3.1ï¼ç©ºæ ¼ -Linuxå†…æ ¸ç„ç©ºæ ¼ä½¿ç”¨æ–¹å¼ï¼ˆä¸»è¦ï¼‰å–决äºå®ƒæ˜¯ç”¨äºå‡½æ•°è¿˜æ˜¯å…³é”®å—ă€‚ï¼ˆå¤§å¤æ•°ï¼‰å…³é”®å—å -è¦å ä¸€ä¸ªç©ºæ ¼ă€‚å€¼å¾—æ³¨æ„ç„例外是sizeofă€typeofă€alignofå’Œ__attribute__ï¼Œè¿™äº›å…³é”®å— -æŸäº›ç¨‹åº¦ä¸çœ‹èµ·æ¥æ›´åƒå‡½æ•°ï¼ˆå®ƒä»¬åœ¨Linux里也常常伴éå°æ‹¬å·è€Œä½¿ç”¨ï¼Œå°½ç®¡åœ¨Cè¯è¨€é‡Œè¿™æ · -ç„å°æ‹¬å·ä¸æ˜¯å¿…需ç„,就åƒâ€œstruct fileinfo infoâ€å£°æ˜è¿‡åç„“sizeof infoâ€ï¼‰ă€‚ +Linux å†…æ ¸ç„ç©ºæ ¼ä½¿ç”¨æ–¹å¼ï¼ˆä¸»è¦ï¼‰å–决äºå®ƒæ˜¯ç”¨äºå‡½æ•°è¿˜æ˜¯å…³é”®å—ă€‚ï¼ˆå¤§å¤æ•°ï¼‰å…³é”®å—å +è¦å ä¸€ä¸ªç©ºæ ¼ă€‚å€¼å¾—æ³¨æ„ç„例外是 sizeofă€typeofă€alignof å’Œ __attribute__,这些 +关键嗿Ÿäº›ç¨‹åº¦ä¸çœ‹èµ·æ¥æ›´åƒå‡½æ•°ï¼ˆå®ƒä»¬åœ¨ Linux 里也常常伴éå°æ‹¬å·è€Œä½¿ç”¨ï¼Œå°½ç®¡åœ¨ C 里 +è¿™æ ·ç„å°æ‹¬å·ä¸æ˜¯å¿…需ç„ï¼Œå°±åƒ â€œstruct fileinfo info†声æ˜è¿‡åç„ â€œsizeof infoâ€ï¼‰ă€‚ 所以在这些关键å—之忔¾ä¸€ä¸ªç©ºæ ¼ï¼ + if, switch, case, for, do, while -但是ä¸è¦åœ¨sizeofă€typeofă€alignof或者__attribute__这些关键å—之忔¾ç©ºæ ¼ă€‚例如, + +但是ä¸è¦åœ¨ sizeofă€typeofă€alignof 或者 __attribute__ 这些关键å—之忔¾ç©ºæ ¼ă€‚例如, + s = sizeof(struct file); ä¸è¦åœ¨å°æ‹¬å·é‡Œç„表达å¼ä¸¤ä¾§å ç©ºæ ¼ă€‚è¿™æ˜¯ä¸€ä¸ªåä¾‹ï¼ s = sizeof( struct file ); -å½“å£°æ˜æŒ‡é’ˆç±»å‹æˆ–è€…è¿”å›æŒ‡é’ˆç±»å‹ç„函数时,“*â€ç„é¦–é€‰ä½¿ç”¨æ–¹å¼æ˜¯ä½¿ä¹‹é è¿‘å˜é‡å或者函 +å½“å£°æ˜æŒ‡é’ˆç±»å‹æˆ–è€…è¿”å›æŒ‡é’ˆç±»å‹ç„函数时,“*†ç„é¦–é€‰ä½¿ç”¨æ–¹å¼æ˜¯ä½¿ä¹‹é è¿‘å˜é‡å或者函 æ•°åï¼Œè€Œä¸æ˜¯é è¿‘ç±»å‹åă€‚ä¾‹åï¼ char *linux_banner; @@ -204,15 +202,18 @@ Linuxå†…æ ¸ç„ç©ºæ ¼ä½¿ç”¨æ–¹å¼ï¼ˆä¸»è¦ï¼‰å–决äºå®ƒæ˜¯ç”¨äºå‡½æ•°è¿˜æ˜¯å…³ = + - < > * / % | & ^ <= >= == != ? : 但是一元æ“作符åä¸è¦å ç©ºæ ¼ï¼ + & * + - ~ ! sizeof typeof alignof __attribute__ defined å缀自å 和自å‡ä¸€å…ƒæ“作符å‰ä¸å ç©ºæ ¼ï¼ + ++ -- å‰ç¼€è‡ªå 和自å‡ä¸€å…ƒæ“作符åä¸å ç©ºæ ¼ï¼ + ++ -- -“.â€å’Œâ€œ->â€ç»“æ„体æˆå‘˜æ“作符å‰åä¸å ç©ºæ ¼ă€‚ +‘.’ å’Œ “->†结æ„体æˆå‘˜æ“作符å‰åä¸å ç©ºæ ¼ă€‚ ä¸è¦åœ¨è¡Œå°¾ç•™ç©ºç™½ă€‚有些å¯ä»¥è‡ªå¨ç¼©è¿›ç„编辑器ä¼åœ¨æ–°è¡Œç„行首å 入适é‡ç„空白,然åä½ å°±å¯ä»¥ç›´æ¥åœ¨é‚£ä¸€è¡Œè¾“入代ç 。ä¸è¿‡å‡å¦‚ä½ æœ€åæ²¡æœ‰åœ¨é‚£ä¸€è¡Œè¾“入代ç ï¼Œæœ‰äº›ç¼–è¾‘å™¨å°±ä¸ @@ -225,23 +226,23 @@ Linuxå†…æ ¸ç„ç©ºæ ¼ä½¿ç”¨æ–¹å¼ï¼ˆä¸»è¦ï¼‰å–决äºå®ƒæ˜¯ç”¨äºå‡½æ•°è¿˜æ˜¯å…³ ç¬¬å››ç« ï¼å‘½å -C是一个简朴ç„è¯è¨€ï¼Œä½ ç„命åä¹Ÿåº”è¯¥è¿™æ ·ă€‚å’ŒModula-2å’ŒPascal程åºå‘˜ä¸åŒï¼ŒC程åºå‘˜ä¸ä½¿ -用类似ThisVariableIsATemporaryCounterè¿™æ ·å丽ç„åå—。C程åºå‘˜ä¼ç§°é‚£ä¸ªå˜é‡ä¸ºâ€œtmp†-ï¼Œè¿™æ ·å†™èµ·æ¥ä¼æ›´å®¹æ˜“,而且至少ä¸ä¼ä»¤å…¶é¾äºç†è§£ă€‚ +C是一个简朴ç„è¯è¨€ï¼Œä½ ç„命åä¹Ÿåº”è¯¥è¿™æ ·ă€‚å’Œ Modula-2 å’Œ Pascal 程åºå‘˜ä¸åŒï¼ŒC 程åºå‘˜ +ä¸ä½¿ç”¨ç±»ä¼¼ ThisVariableIsATemporaryCounter è¿™æ ·å丽ç„åå—。C 程åºå‘˜ä¼ç§°é‚£ä¸ªå˜é‡ +为 “tmpâ€ï¼Œè¿™æ ·å†™èµ·æ¥ä¼æ›´å®¹æ˜“,而且至少ä¸ä¼ä»¤å…¶é¾äºç†è§£ă€‚ ä¸è¿‡ï¼Œè™½ç„¶æ··ç”¨å¤§å°å†™ç„åå—æ˜¯ä¸æå€¡ä½¿ç”¨ç„,但是全局å˜é‡è¿˜æ˜¯éœ€è¦ä¸€ä¸ªå…·æè¿°æ€§ç„åå— -ă€‚ç§°ä¸€ä¸ªå…¨å±€å‡½æ•°ä¸ºâ€œfooâ€æ˜¯ä¸€ä¸ªé¾ä»¥é¥¶æ•ç„é”™è¯¯ă€‚ +ă€‚ç§°ä¸€ä¸ªå…¨å±€å‡½æ•°ä¸º “foo†是一个é¾ä»¥é¥¶æ•ç„é”™è¯¯ă€‚ 全局å˜é‡ï¼ˆåªæœ‰å½“ä½ çœŸæ£éœ€è¦å®ƒä»¬ç„时候å†ç”¨å®ƒï¼‰éœ€è¦æœ‰ä¸€ä¸ªå…·æè¿°æ€§ç„åå—,就åƒå…¨å±€å‡½ -æ•°ă€‚å¦‚æœä½ 有一个å¯ä»¥è®¡ç®—æ´»å¨ç”¨æˆ·æ•°é‡ç„å‡½æ•°ï¼Œä½ åº”è¯¥å«å®ƒâ€œcount_active_users()â€æˆ–者 -类似ç„åå—ï¼Œä½ ä¸åº”该å«å®ƒâ€œcntuser()â€ă€‚ +æ•°ă€‚å¦‚æœä½ 有一个å¯ä»¥è®¡ç®—æ´»å¨ç”¨æˆ·æ•°é‡ç„å‡½æ•°ï¼Œä½ åº”è¯¥å«å®ƒ “count_active_users()†+或者类似ç„åå—ï¼Œä½ ä¸åº”该å«å®ƒ “cntuser()â€ă€‚ 在函数åä¸åŒ…å«å‡½æ•°ç±»å‹ï¼ˆæ‰€è°“ç„åŒˆç‰™åˆ©å‘½åæ³•)是脑å出了问题——编译器知é“那些类å‹è€Œ 且能够检查那些类å‹ï¼Œè¿™æ ·ååªèƒ½æç¨‹åºå‘˜å¼„ç³æ¶‚äº†ă€‚é¾æ€ªå¾®è½¯æ€»æ˜¯åˆ¶é€ 出有问题ç„程åºă€‚ 本地å˜é‡å应该简çŸï¼Œè€Œä¸”能够表达相关ç„å«ä¹‰ă€‚如æœä½ æœ‰ä¸€äº›éæœºç„æ•´æ•°å‹ç„循ç¯è®¡æ•°å™¨ -,它应该被称为“iâ€ă€‚å«å®ƒâ€œloop_counterâ€å¹¶æ— ç›å¤„,如æœå®ƒæ²¡æœ‰è¢«è¯¯è§£ç„å¯èƒ½ç„è¯ă€‚类似 -ç„,“tmpâ€å¯ä»¥ç”¨æ¥ç§°å‘¼ä»»æ„ç±»å‹ç„临时å˜é‡ă€‚ +,它应该被称为 “iâ€ă€‚å«å®ƒ “loop_counterâ€ å¹¶æ— ç›å¤„,如æœå®ƒæ²¡æœ‰è¢«è¯¯è§£ç„å¯èƒ½ç„è¯ă€‚ +类似ç„,“tmp†å¯ä»¥ç”¨æ¥ç§°å‘¼ä»»æ„ç±»å‹ç„临时å˜é‡ă€‚ 如æœä½ æ€•æ··æ·†äº†ä½ ç„æœ¬åœ°å˜é‡åï¼Œä½ å°±é‡åˆ°å¦ä¸€ä¸ªé—®é¢˜äº†ï¼Œå«å函数å¢é•¿è·å°”蒙失衡综åˆç—‡ ă€‚è¯·çœ‹ç¬¬å…ç« ï¼ˆå‡½æ•°ï¼‰ă€‚ @@ -249,9 +250,9 @@ C是一个简朴ç„è¯è¨€ï¼Œä½ ç„命åä¹Ÿåº”è¯¥è¿™æ ·ă€‚å’ŒModula-2å’ŒPascalç¨ ç¬¬äº”ç« ï¼Typedef -ä¸è¦ä½¿ç”¨ç±»ä¼¼â€œvps_tâ€ä¹‹ç±»ç„ä¸œè¥¿ă€‚ +ä¸è¦ä½¿ç”¨ç±»ä¼¼ “vps_t†之类ç„ä¸œè¥¿ă€‚ -对结æ„体和指针使用typedefæ˜¯ä¸€ä¸ªé”™è¯¯ă€‚å½“ä½ åœ¨ä»£ç é‡Œçœ‹åˆ°ï¼ +对结æ„体和指针使用 typedef æ˜¯ä¸€ä¸ªé”™è¯¯ă€‚å½“ä½ åœ¨ä»£ç é‡Œçœ‹åˆ°ï¼ vps_t a; @@ -261,91 +262,91 @@ C是一个简朴ç„è¯è¨€ï¼Œä½ ç„命åä¹Ÿåº”è¯¥è¿™æ ·ă€‚å’ŒModula-2å’ŒPascalç¨ struct virtual_container *a; -ä½ å°±çŸ¥é““aâ€æ˜¯ä»€ä¹ˆäº†ă€‚ +ä½ å°±çŸ¥é“ â€œaâ€ æ˜¯ä»€ä¹ˆäº†ă€‚ -很å¤äººè®¤ä¸ºtypedef“能æé«˜å¯è¯»æ€§â€ă€‚å®é™…䏿˜¯è¿™æ ·ç„ă€‚å®ƒä»¬åªåœ¨ä¸‹åˆ—æƒ…å†µä¸‹æœ‰ç”¨ï¼ +很å¤äººè®¤ä¸º typedef “能æé«˜å¯è¯»æ€§â€ă€‚å®é™…䏿˜¯è¿™æ ·ç„ă€‚å®ƒä»¬åªåœ¨ä¸‹åˆ—æƒ…å†µä¸‹æœ‰ç”¨ï¼ - (a) 完全ä¸é€æ˜ç„å¯¹è±¡ï¼ˆè¿™ç§æƒ…况下è¦ä¸»å¨ä½¿ç”¨typedefæ¥éè—这个对象å®é™…䏿˜¯ä»€ä¹ˆï¼‰ă€‚ + (a) 完全ä¸é€æ˜ç„å¯¹è±¡ï¼ˆè¿™ç§æƒ…况下è¦ä¸»å¨ä½¿ç”¨ typedef æ¥éè—这个对象å®é™…䏿˜¯ä»€ä¹ˆï¼‰ă€‚ - 例如ï¼â€œpte_tâ€ç‰ä¸é€æ˜å¯¹è±¡ï¼Œä½ åªèƒ½ç”¨åˆé€‚ç„访问函数æ¥è®¿é—®å®ƒä»¬ă€‚ + 例如ï¼â€œpte_t†ç‰ä¸é€æ˜å¯¹è±¡ï¼Œä½ åªèƒ½ç”¨åˆé€‚ç„访问函数æ¥è®¿é—®å®ƒä»¬ă€‚ - 注æ„ï¼ä¸é€æ˜æ€§å’Œâ€œè®¿é—®å‡½æ•°â€æœ¬èº«æ˜¯ä¸å¥½ç„ă€‚æˆ‘ä»¬ä½¿ç”¨pte_tç‰ç±»å‹ç„åŸå› 在äºçœŸç„是 + 注æ„ï¼ä¸é€æ˜æ€§å’Œâ€œè®¿é—®å‡½æ•°â€æœ¬èº«æ˜¯ä¸å¥½ç„ă€‚æˆ‘ä»¬ä½¿ç”¨ pte_t ç‰ç±»å‹ç„åŸå› 在äºçœŸç„是 完全没有任何共用ç„å¯è®¿é—®ä¿¡æ¯ă€‚ - (b) 清æ¥ç„æ•´æ•°ç±»å‹ï¼Œå¦‚æ¤ï¼Œè¿™å±‚æ½è±¡å°±å¯ä»¥å¸®å©æ¶ˆé™¤åˆ°åº•是“intâ€è¿˜æ˜¯â€œlongâ€ç„æ··æ·†ă€‚ + (b) 清æ¥ç„æ•´æ•°ç±»å‹ï¼Œå¦‚æ¤ï¼Œè¿™å±‚æ½è±¡å°±å¯ä»¥å¸®å©æ¶ˆé™¤åˆ°åº•是 “int†还是 “longâ€ ç„æ··æ·†ă€‚ - u8/u16/u32是完全没有问题ç„typedef,ä¸è¿‡å®ƒä»¬æ›´ç¬¦åˆç±»åˆ«(d)è€Œä¸æ˜¯è¿™é‡Œă€‚ + u8/u16/u32 æ˜¯å®Œå…¨æ²¡æœ‰é—®é¢˜ç„ typedef,ä¸è¿‡å®ƒä»¬æ›´ç¬¦åˆç±»åˆ« (d) è€Œä¸æ˜¯è¿™é‡Œă€‚ - 冿¬¡æ³¨æ„ï¼è¦è¿™æ ·åï¼Œå¿…é¡»äº‹å‡ºæœ‰å› ă€‚å¦‚æœæŸä¸ªå˜é‡æ˜¯â€œunsigned longâ€œï¼Œé‚£ä¹ˆæ²¡æœ‰å¿…è¦ + 冿¬¡æ³¨æ„ï¼è¦è¿™æ ·åï¼Œå¿…é¡»äº‹å‡ºæœ‰å› ă€‚å¦‚æœæŸä¸ªå˜é‡æ˜¯ “unsigned longâ€œï¼Œé‚£ä¹ˆæ²¡æœ‰å¿…è¦ typedef unsigned long myflags_t; - ä¸è¿‡å¦‚æœæœ‰ä¸€ä¸ªæ˜ç¡®ç„åŸå› ,比如它在æŸç§æƒ…况下å¯èƒ½ä¼æ˜¯ä¸€ä¸ªâ€œunsigned intâ€è€Œåœ¨ - 其他情况下å¯èƒ½ä¸ºâ€œunsigned longâ€ï¼Œé‚£ä¹ˆå°±ä¸è¦ç¹è±«ï¼Œè¯·å¡å¿…使用typedef。 + ä¸è¿‡å¦‚æœæœ‰ä¸€ä¸ªæ˜ç¡®ç„åŸå› ,比如它在æŸç§æƒ…况下å¯èƒ½ä¼æ˜¯ä¸€ä¸ª “unsigned int†而在 + 其他情况下å¯èƒ½ä¸º “unsigned longâ€ï¼Œé‚£ä¹ˆå°±ä¸è¦ç¹è±«ï¼Œè¯·å¡å¿…使用 typedef。 (c) å½“ä½ ä½¿ç”¨sparse按å—é¢ç„åˆ›å»ºä¸€ä¸ªæ–°ç±»å‹æ¥åç±»å‹æ£€æŸ¥ç„æ—¶å€™ă€‚ (d) å’Œæ ‡å‡†C99ç±»å‹ç›¸åŒç„ç±»å‹ï¼Œåœ¨æŸäº›ä¾‹å¤–ç„æƒ…å†µä¸‹ă€‚ - 虽然让眼ç›å’Œè„‘ç‹æ¥é€‚åº”æ–°ç„æ ‡å‡†ç±»å‹æ¯”如“uint32_tâ€ä¸éœ€è¦è±å¾ˆå¤æ—¶é—´ï¼Œå¯æ˜¯æœ‰äº› + 虽然让眼ç›å’Œè„‘ç‹æ¥é€‚åº”æ–°ç„æ ‡å‡†ç±»å‹æ¯”如 “uint32_t†ä¸éœ€è¦è±å¾ˆå¤æ—¶é—´ï¼Œå¯æ˜¯æœ‰äº› 人ä»ç„¶æ‹’ç»ä½¿ç”¨å®ƒä»¬ă€‚ - å› æ¤ï¼ŒLinux特有ç„ç‰åŒäºæ ‡å‡†ç±»å‹ç„“u8/u16/u32/u64â€ç±»å‹å’Œå®ƒä»¬ç„有符å·ç±»å‹æ˜¯è¢« + å› æ¤ï¼ŒLinux 特有ç„ç‰åŒäºæ ‡å‡†ç±»å‹ç„ “u8/u16/u32/u64†类å‹å’Œå®ƒä»¬ç„有符å·ç±»å‹æ˜¯è¢« å…许ç„â€”â€”å°½ç®¡åœ¨ä½ è‡ªå·±ç„æ–°ä»£ç ä¸ï¼Œå®ƒä»¬ä¸æ˜¯å¼ºåˆ¶è¦æ±‚è¦ä½¿ç”¨ç„。 当编辑已ç»ä½¿ç”¨äº†æŸä¸ªç±»å‹é›†ç„å·²æœ‰ä»£ç æ—¶ï¼Œä½ 应该éµå¾ªé‚£äº›ä»£ç ä¸å·²ç»å出ç„é€‰æ‹©ă€‚ (e) å¯ä»¥åœ¨ç”¨æˆ·ç©ºé—´å®‰å…¨ä½¿ç”¨ç„ç±»å‹ă€‚ - 在æŸäº›ç”¨æˆ·ç©ºé—´å¯è§ç„结æ„体里,我们ä¸èƒ½è¦æ±‚C99ç±»å‹è€Œä¸”ä¸èƒ½ç”¨ä¸é¢æåˆ°ç„“u32†- ç±»å‹ă€‚å› æ¤ï¼Œæˆ‘们在ä¸ç”¨æˆ·ç©ºé—´å…±äº«ç„所有结æ„体ä¸ä½¿ç”¨__u32和类似ç„ç±»å‹ă€‚ + 在æŸäº›ç”¨æˆ·ç©ºé—´å¯è§ç„结æ„体里,我们ä¸èƒ½è¦æ±‚C99ç±»å‹è€Œä¸”ä¸èƒ½ç”¨ä¸é¢æåˆ°ç„ “u32†+ ç±»å‹ă€‚å› æ¤ï¼Œæˆ‘们在ä¸ç”¨æˆ·ç©ºé—´å…±äº«ç„所有结æ„体ä¸ä½¿ç”¨ __u32 和类似ç„ç±»å‹ă€‚ -å¯èƒ½è¿˜æœ‰å…¶ä»–ç„æƒ…况,ä¸è¿‡åŸºæœ¬ç„规则是永远ä¸è¦ä½¿ç”¨typedef,除éä½ å¯ä»¥æ˜ç¡®ç„åº”ç”¨ä¸ +å¯èƒ½è¿˜æœ‰å…¶ä»–ç„æƒ…况,ä¸è¿‡åŸºæœ¬ç„规则是永远ä¸è¦ä½¿ç”¨ typedef,除éä½ å¯ä»¥æ˜ç¡®ç„åº”ç”¨ä¸ è¿°æŸä¸ªè§„则ä¸ç„ä¸€ä¸ªă€‚ æ€»ç„æ¥è¯´ï¼Œå¦‚æœä¸€ä¸ªæŒ‡é’ˆæˆ–者一个结æ„体里ç„å…ƒç´ å¯ä»¥åˆç†ç„被直æ¥è®¿é—®åˆ°ï¼Œé‚£ä¹ˆå®ƒä»¬å°±ä¸ -应该是一个typedef。 +应该是一个 typedef。 第å…ç« ï¼å‡½æ•° 函数应该简çŸè€Œæ¼‚亮,并且åªå®Œæˆä¸€ä»¶äº‹æƒ…ă€‚å‡½æ•°åº”è¯¥å¯ä»¥ä¸€å±æˆ–è€…ä¸¤å±æ˜¾ç¤ºå®Œï¼ˆæˆ‘们都知 -é“ISO/ANSIå±å¹•大尿˜¯80x24),åªå一件事情,而且æå®ƒåå¥½ă€‚ +é“ ISO/ANSI å±å¹•大尿˜¯ 80x24),åªå一件事情,而且æå®ƒåå¥½ă€‚ ä¸€ä¸ªå‡½æ•°ç„æœ€å¤§é•¿åº¦æ˜¯å’Œè¯¥å‡½æ•°ç„夿‚度和缩进级数æˆå比ç„ă€‚æ‰€ä»¥ï¼Œå¦‚æœä½ 有一个ç†è®ºä¸ -很简å•ç„åªæœ‰ä¸€ä¸ªå¾ˆé•¿ï¼ˆä½†æ˜¯ç®€å•)ç„caseè¯å¥ç„å‡½æ•°ï¼Œè€Œä¸”ä½ éœ€è¦åœ¨æ¯ä¸ªcase里å很å¤å¾ˆ -å°ç„äº‹æƒ…ï¼Œè¿™æ ·ç„函数尽管很长,但也是å¯ä»¥ç„。 +很简å•ç„åªæœ‰ä¸€ä¸ªå¾ˆé•¿ï¼ˆä½†æ˜¯ç®€å•ï¼‰ç„ case è¯å¥ç„å‡½æ•°ï¼Œè€Œä¸”ä½ éœ€è¦åœ¨æ¯ä¸ª case 里å +很å¤å¾ˆå°ç„äº‹æƒ…ï¼Œè¿™æ ·ç„函数尽管很长,但也是å¯ä»¥ç„。 ä¸è¿‡ï¼Œå¦‚æœä½ æœ‰ä¸€ä¸ªå¤æ‚ç„å‡½æ•°ï¼Œè€Œä¸”ä½ æ€€ç–‘ä¸€ä¸ªå¤©åˆ†ä¸æ˜¯å¾ˆé«˜ç„高ä¸ä¸€å¹´çº§å¦ç”Ÿå¯èƒ½ç”至 æä¸æ¸…æ¥è¿™ä¸ªå‡½æ•°ç„ç›®ç„ï¼Œä½ åº”è¯¥ä¸¥æ ¼ç„éµå®ˆå‰é¢æåˆ°ç„长度é™åˆ¶ă€‚使用辅å©å‡½æ•°ï¼Œå¹¶ä¸ºä¹‹ å–个具æè¿°æ€§ç„åå—(如æœä½ è§‰å¾—å®ƒä»¬ç„æ€§èƒ½å¾ˆé‡è¦ç„è¯ï¼Œå¯ä»¥è®©ç¼–译器内è”å®ƒä»¬ï¼Œè¿™æ ·ç„ æ•ˆæœå¾€å¾€ä¼æ¯”ä½ å†™ä¸€ä¸ªå¤æ‚å‡½æ•°ç„æ•ˆæœè¦å¥½ă€‚) -函数ç„å¦å¤–ä¸€ä¸ªè¡¡é‡æ ‡å‡†æ˜¯æœ¬åœ°å˜é‡ç„æ•°é‡ă€‚æ¤æ•°é‡ä¸åº”超过5ï¼10个,å¦åˆ™ä½ ç„函数就有 +函数ç„å¦å¤–ä¸€ä¸ªè¡¡é‡æ ‡å‡†æ˜¯æœ¬åœ°å˜é‡ç„æ•°é‡ă€‚æ¤æ•°é‡ä¸åº”超过 5ï¼10 个,å¦åˆ™ä½ ç„函数就有 é—®é¢˜äº†ă€‚é‡æ–°è€ƒè™‘ä¸€ä¸‹ä½ ç„函数,æå®ƒåˆ†æ‹†æˆæ›´å°ç„å‡½æ•°ă€‚äººç„大脑一般å¯ä»¥è½»æ¾ç„åŒæ—¶è·Ÿ -踪7个ä¸åŒç„事物,如æœå†å¢å¤ç„è¯ï¼Œå°±ä¼ç³æ¶‚äº†ă€‚å³ä¾¿ä½ èªé¢–è¿‡äººï¼Œä½ ä¹Ÿå¯èƒ½ä¼è®°ä¸æ¸…ä½ 2 -个星期å‰å过ç„äº‹æƒ…ă€‚ +踪 7 个ä¸åŒç„事物,如æœå†å¢å¤ç„è¯ï¼Œå°±ä¼ç³æ¶‚äº†ă€‚å³ä¾¿ä½ èªé¢–è¿‡äººï¼Œä½ ä¹Ÿå¯èƒ½ä¼è®°ä¸æ¸…ä½ +2 个星期å‰å过ç„äº‹æƒ…ă€‚ -åœ¨æºæ–‡ä»¶é‡Œï¼Œä½¿ç”¨ç©ºè¡Œé”å¼€ä¸åŒç„å‡½æ•°ă€‚å¦‚æœè¯¥å‡½æ•°éœ€è¦è¢«å¯¼å‡ºï¼Œå®ƒç„EXPORT*å®åº”该紧贴 +åœ¨æºæ–‡ä»¶é‡Œï¼Œä½¿ç”¨ç©ºè¡Œé”å¼€ä¸åŒç„å‡½æ•°ă€‚å¦‚æœè¯¥å‡½æ•°éœ€è¦è¢«å¯¼å‡ºï¼Œå®ƒç„ EXPORT* å®åº”该紧贴 在它ç„结æŸå¤§æ‹¬å·ä¹‹ä¸‹ă€‚æ¯”å¦‚ï¼ -int system_is_up(void) -{ - return system_state == SYSTEM_RUNNING; -} -EXPORT_SYMBOL(system_is_up); + int system_is_up(void) + { + return system_state == SYSTEM_RUNNING; + } + EXPORT_SYMBOL(system_is_up); -在函数åŸå‹ä¸ï¼ŒåŒ…å«å‡½æ•°åå’Œå®ƒä»¬ç„æ•°æ®ç±»å‹ă€‚虽然Cè¯è¨€é‡Œæ²¡æœ‰è¿™æ ·ç„è¦æ±‚,在Linux里这 +在函数åŸå‹ä¸ï¼ŒåŒ…å«å‡½æ•°åå’Œå®ƒä»¬ç„æ•°æ®ç±»å‹ă€‚虽然Cè¯è¨€é‡Œæ²¡æœ‰è¿™æ ·ç„è¦æ±‚,在 Linux 里这 是æå€¡ç„åæ³•ï¼Œå› ä¸ºè¿™æ ·å¯ä»¥å¾ˆç®€å•ç„给读者æä¾›æ›´å¤ç„有价值ç„ä¿¡æ¯ă€‚ ç¬¬ä¸ƒç« ï¼é›†ä¸ç„函数退出途径 -虽然被æŸäº›äººå£°ç§°å·²ç»è¿‡æ—¶ï¼Œä½†æ˜¯gotoè¯å¥ç„ç‰ä»·ç‰©è¿˜æ˜¯ç»å¸¸è¢«ç¼–è¯‘å™¨æ‰€ä½¿ç”¨ï¼Œå…·ä½“å½¢å¼æ˜¯ +虽然被æŸäº›äººå£°ç§°å·²ç»è¿‡æ—¶ï¼Œä½†æ˜¯ goto è¯å¥ç„ç‰ä»·ç‰©è¿˜æ˜¯ç»å¸¸è¢«ç¼–è¯‘å™¨æ‰€ä½¿ç”¨ï¼Œå…·ä½“å½¢å¼æ˜¯ æ— æ¡ä»¶è·³è½¬æŒ‡ä»¤ă€‚ -当一个函数ä»å¤ä¸ªä½ç½®é€€å‡ºå¹¶ä¸”需è¦å一些é€ç”¨ç„清ç†å·¥ä½œç„时候,gotoç„好处就显ç°å‡ºæ¥ -äº†ă€‚ +当一个函数ä»å¤ä¸ªä½ç½®é€€å‡ºï¼Œå¹¶ä¸”需è¦å一些类似清ç†ç„å¸¸è§æ“作时,goto è¯å¥å°±å¾ˆæ–¹ä¾¿äº†ă€‚ +如æœå¹¶ä¸éœ€è¦æ¸…ç†æ“ä½œï¼Œé‚£ä¹ˆç›´æ¥ return å³å¯ă€‚ ç†ç”±æ˜¯ï¼ @@ -354,26 +355,37 @@ EXPORT_SYMBOL(system_is_up); - å¯ä»¥é¿å…ç”±äºä¿®æ”¹æ—¶å¿˜è®°æ›´æ–°æŸä¸ªå•独ç„退出点而导致ç„错误 - å‡è½»äº†ç¼–译器ç„å·¥ä½œï¼Œæ— éœ€åˆ é™¤å†—ä½™ä»£ç ;) -int fun(int a) -{ - int result = 0; - char *buffer = kmalloc(SIZE); - - if (buffer == NULL) - return -ENOMEM; - - if (condition1) { - while (loop1) { - ... + int fun(int a) + { + int result = 0; + char *buffer; + + buffer = kmalloc(SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + if (condition1) { + while (loop1) { + ... + } + result = 1; + goto out_buffer; } - result = 1; - goto out; + ... + out_buffer: + kfree(buffer); + return result; } - ... -out: - kfree(buffer); - return result; -} + +ä¸€ä¸ªéœ€è¦æ³¨æ„ç„常è§é”™è¯¯æ˜¯â€œä¸€ä¸ª err 错误â€ï¼Œå°±åƒè¿™æ ·ï¼ + + err: + kfree(foo->bar); + kfree(foo); + return ret; + +这段代ç ç„错误是,在æŸäº›é€€å‡ºè·¯å¾„ä¸ â€œfoo†是 NULL。é€å¸¸æƒ…况下,é€è¿‡æå®ƒåˆ†ç¦»æˆä¸¤ä¸ª +é”™è¯¯æ ‡ç¾ â€œerr_bar:†和 “err_foo:†æ¥ä¿®å¤è¿™ä¸ªé”™è¯¯ă€‚ ç¬¬å…«ç« ï¼æ³¨é‡ @@ -386,10 +398,10 @@ out: å 太å¤ă€‚ä½ åº”è¯¥åç„ï¼Œæ˜¯ææ³¨é‡æ”¾åœ¨å‡½æ•°ç„头部,å‘诉人们它å了什么,也å¯ä»¥å ä¸å®ƒåè¿™ 些事情ç„åŸå› 。 -当注é‡å†…æ ¸API函数时,请使用kernel-docæ ¼å¼ă€‚请看 -Documentation/kernel-doc-nano-HOWTO.txtå’Œscripts/kernel-doc以è·å¾—详细信æ¯ă€‚ +当注é‡å†…æ ¸API函数时,请使用 kernel-doc æ ¼å¼ă€‚请看 +Documentation/kernel-doc-nano-HOWTO.txtå’Œscripts/kernel-doc 以è·å¾—详细信æ¯ă€‚ -Linuxç„æ³¨é‡é£æ ¼æ˜¯C89“/* ... */â€é£æ ¼ă€‚ä¸è¦ä½¿ç”¨C99飿 ¼â€œ// ...â€æ³¨é‡ă€‚ +Linuxç„æ³¨é‡é£æ ¼æ˜¯ C89 “/* ... */â€ é£æ ¼ă€‚ä¸è¦ä½¿ç”¨ C99 飿 ¼ “// ...†注é‡ă€‚ 长(å¤è¡Œï¼‰ç„首选注é‡é£æ ¼æ˜¯ï¼ @@ -402,6 +414,15 @@ Linuxç„æ³¨é‡é£æ ¼æ˜¯C89“/* ... */â€é£æ ¼ă€‚ä¸è¦ä½¿ç”¨C99飿 ¼â€œ// ... * with beginning and ending almost-blank lines. */ +对äºåœ¨ net/ å’Œ drivers/net/ ç„æ–‡ä»¶ï¼Œé¦–选ç„长(å¤è¡Œï¼‰æ³¨é‡é£æ ¼æœ‰äº›ä¸åŒă€‚ + + /* The preferred comment style for files in net/ and drivers/net + * looks like this. + * + * It is nearly the same as the generally preferred comment style, + * but there is no initial almost-blank line. + */ + æ³¨é‡æ•°æ®ä¹Ÿæ˜¯å¾ˆé‡è¦ç„,ä¸ç®¡æ˜¯åŸºæœ¬ç±»å‹è¿˜æ˜¯è¡ç”Ÿç±»å‹ă€‚为了方便å®ç°è¿™ä¸€ç‚¹ï¼Œæ¯ä¸€è¡Œåº”åª å£°æ˜ä¸€ä¸ªæ•°æ®ï¼ˆä¸è¦ä½¿ç”¨é€—å·æ¥ä¸€æ¬¡å£°æ˜å¤ä¸ªæ•°æ®ï¼‰ă€‚è¿™æ ·ä½ å°±æœ‰ç©ºé—´æ¥ä¸ºæ¯ä¸ªæ•°æ®å†™ä¸€æ®µ å°æ³¨é‡æ¥è§£é‡å®ƒä»¬ç„ç”¨é€”äº†ă€‚ @@ -409,49 +430,63 @@ Linuxç„æ³¨é‡é£æ ¼æ˜¯C89“/* ... */â€é£æ ¼ă€‚ä¸è¦ä½¿ç”¨C99飿 ¼â€œ// ... 第ä¹ç« ï¼ä½ å·²ç»æäº‹æƒ…å¼„ç³Ÿäº† -è¿™æ²¡ä»€ä¹ˆï¼Œæˆ‘ä»¬éƒ½æ˜¯è¿™æ ·ă€‚å¯èƒ½ä½ ç„使用了很长时间Unixç„æœ‹å‹å·²ç»å‘è¯‰ä½ â€œGNU emacsâ€èƒ½ -自å¨å¸®ä½ æ ¼å¼åŒ–Cæºä»£ç ï¼Œè€Œä¸”ä½ ä¹Ÿæ³¨æ„åˆ°äº†ï¼Œç¡®å®æ˜¯è¿™æ ·ï¼Œä¸è¿‡å®ƒæ‰€ä½¿ç”¨ç„默认值和我们 -想è¦ç„相å»ç”远(å®é™…ä¸ï¼Œç”è‡³æ¯”éæœºæ‰“ç„还è¦å·®â€”â€”æ— æ•°ä¸ªçŒ´å在GNU emacsé‡Œæ‰“å—æ°¸è¿œä¸ -ä¼åˆ›é€ 出一个好程åºï¼‰ï¼ˆè¯‘注ï¼è¯·å‚考Infinite Monkey Theorem) - -æ‰€ä»¥ä½ è¦ä¹ˆæ”¾å¼ƒGNU emacs,è¦ä¹ˆæ”¹å˜å®ƒè®©å®ƒä½¿ç”¨æ›´åˆç†ç„设å®ă€‚è¦é‡‡ç”¨åä¸€ä¸ªæ–¹æ¡ˆï¼Œä½ å¯ -以æä¸‹é¢è¿™æ®µç²˜è´´åˆ°ä½ ç„.emacsæ–‡ä»¶é‡Œă€‚ - -(defun linux-c-mode () - "C mode with adjusted defaults for use with the Linux kernel." - (interactive) - (c-mode) - (c-set-style "K&R") - (setq tab-width 8) - (setq indent-tabs-mode t) - (setq c-basic-offset 8)) - -è¿™æ ·å°±å®ä¹‰äº†M-x linux-c-modeå‘½ä»¤ă€‚å½“ä½ hack一个模å—ç„æ—¶å€™ï¼Œå¦‚æœä½ æå—符串 --*- linux-c -*-æ”¾åœ¨å¤´ä¸¤è¡Œç„æŸä¸ªä½ç½®ï¼Œè¿™ä¸ªæ¨¡å¼å°†ä¼è¢«è‡ªå¨è°ƒç”¨ă€‚如æœä½ å¸Œæœ›åœ¨ä½ ä¿®æ”¹ -/usr/src/linuxé‡Œç„æ–‡ä»¶æ—¶é”æœ¯èˆ¬è‡ªå¨æ‰“å¼€linux-c-modeç„è¯ï¼Œä½ 也å¯èƒ½éœ€è¦æ·»å - -(setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.[ch]$" . linux-c-mode) - auto-mode-alist)) - -åˆ°ä½ ç„.emacsæ–‡ä»¶é‡Œă€‚ - -ä¸è¿‡å°±ç®—ä½ å°è¯•让emacsæ£ç¡®ç„æ ¼å¼åŒ–代ç å¤±è´¥äº†ï¼Œä¹Ÿå¹¶ä¸æ„味ç€ä½ 失å»äº†ä¸€åˆ‡ï¼è¿˜å¯ä»¥ç”¨â€œ -indentâ€ă€‚ - -ä¸è¿‡ï¼ŒGNU indent也有和GNU emacsä¸€æ ·æœ‰é—®é¢˜ç„设å®ï¼Œæ‰€ä»¥ä½ 需è¦ç»™å®ƒä¸€äº›å‘½ä»¤é€‰é¡¹ă€‚ä¸ -过,这还ä¸ç®—å¤ªç³Ÿç³•ï¼Œå› ä¸ºå°±ç®—æ˜¯GNU indentç„作者也认åŒK&Rç„æƒå¨æ€§ï¼ˆGNUç„äººå¹¶ä¸æ˜¯å -äººï¼Œä»–ä»¬åªæ˜¯åœ¨è¿™ä¸ªé—®é¢˜ä¸è¢«ä¸¥é‡ç„è¯¯å¯¼äº†ï¼‰ï¼Œæ‰€ä»¥ä½ åªè¦ç»™indent指å®é€‰é¡¹â€œ-kr -i8†-(代表“K&R,8个å—符缩进â€ï¼‰ï¼Œæˆ–者使用“scripts/Lindentâ€ï¼Œè¿™æ ·å°±å¯ä»¥ä»¥æœ€æ—¶é«¦ç„æ–¹å¼ +è¿™æ²¡ä»€ä¹ˆï¼Œæˆ‘ä»¬éƒ½æ˜¯è¿™æ ·ă€‚å¯èƒ½ä½ ç„使用了很长时间 Unix ç„æœ‹å‹å·²ç»å‘è¯‰ä½ â€œGNU emacs†能 +自å¨å¸®ä½ æ ¼å¼åŒ– C æºä»£ç ï¼Œè€Œä¸”ä½ ä¹Ÿæ³¨æ„åˆ°äº†ï¼Œç¡®å®æ˜¯è¿™æ ·ï¼Œä¸è¿‡å®ƒæ‰€ä½¿ç”¨ç„默认值和我们 +想è¦ç„相å»ç”远(å®é™…ä¸ï¼Œç”è‡³æ¯”éæœºæ‰“ç„还è¦å·®â€”â€”æ— æ•°ä¸ªçŒ´å在 GNU emacs é‡Œæ‰“å—æ°¸è¿œä¸ +ä¼åˆ›é€ 出一个好程åºï¼‰ï¼ˆè¯‘注ï¼è¯·å‚考 Infinite Monkey Theorem) + +æ‰€ä»¥ä½ è¦ä¹ˆæ”¾å¼ƒ GNU emacs,è¦ä¹ˆæ”¹å˜å®ƒè®©å®ƒä½¿ç”¨æ›´åˆç†ç„设å®ă€‚è¦é‡‡ç”¨åä¸€ä¸ªæ–¹æ¡ˆï¼Œä½ å¯ +以æä¸‹é¢è¿™æ®µç²˜è´´åˆ°ä½ ç„ .emacs æ–‡ä»¶é‡Œă€‚ + +(defun c-lineup-arglist-tabs-only (ignored) + "Line up argument lists by tabs, not spaces" + (let* ((anchor (c-langelem-pos c-syntactic-element)) + (column (c-langelem-2nd-pos c-syntactic-element)) + (offset (- (1+ column) anchor)) + (steps (floor offset c-basic-offset))) + (* (max steps 1) + c-basic-offset))) + +(add-hook 'c-mode-common-hook + (lambda () + ;; Add kernel style + (c-add-style + "linux-tabs-only" + '("linux" (c-offsets-alist + (arglist-cont-nonempty + c-lineup-gcc-asm-reg + c-lineup-arglist-tabs-only)))))) + +(add-hook 'c-mode-hook + (lambda () + (let ((filename (buffer-file-name))) + ;; Enable kernel mode for the appropriate files + (when (and filename + (string-match (expand-file-name "~/src/linux-trees") + filename)) + (setq indent-tabs-mode t) + (setq show-trailing-whitespace t) + (c-set-style "linux-tabs-only"))))) + +è¿™ä¼è®© emacs 在 ~/src/linux-trees ç›®å½•ä¸‹ç„ C æºæ–‡ä»¶è·å¾—更好ç„å†…æ ¸ä»£ç 飿 ¼ă€‚ + +ä¸è¿‡å°±ç®—ä½ å°è¯•让 emacs æ£ç¡®ç„æ ¼å¼åŒ–代ç å¤±è´¥äº†ï¼Œä¹Ÿå¹¶ä¸æ„味ç€ä½ 失å»äº†ä¸€åˆ‡ï¼è¿˜å¯ä»¥ç”¨ +“indentâ€ă€‚ + +ä¸è¿‡ï¼ŒGNU indent 也有和 GNU emacs ä¸€æ ·æœ‰é—®é¢˜ç„设å®ï¼Œæ‰€ä»¥ä½ 需è¦ç»™å®ƒä¸€äº›å‘½ä»¤é€‰é¡¹ă€‚ä¸ +过,这还ä¸ç®—å¤ªç³Ÿç³•ï¼Œå› ä¸ºå°±ç®—æ˜¯ GNU indent ç„ä½œè€…ä¹Ÿè®¤åŒ K&R ç„æƒå¨æ€§ï¼ˆGNU ç„äººå¹¶ä¸æ˜¯ +åäººï¼Œä»–ä»¬åªæ˜¯åœ¨è¿™ä¸ªé—®é¢˜ä¸è¢«ä¸¥é‡ç„è¯¯å¯¼äº†ï¼‰ï¼Œæ‰€ä»¥ä½ åªè¦ç»™ indent 指å®é€‰é¡¹ “-kr -i8†+(代表 “K&R,8 个å—符缩进â€ï¼‰ï¼Œæˆ–者使用 “scripts/Lindentâ€ï¼Œè¿™æ ·å°±å¯ä»¥ä»¥æœ€æ—¶é«¦ç„æ–¹å¼ ç¼©è¿›æºä»£ç 。 -“indentâ€æœ‰å¾ˆå¤é€‰é¡¹ï¼Œç‰¹åˆ«æ˜¯é‡æ–°æ ¼å¼åŒ–注é‡ç„æ—¶å€™ï¼Œä½ å¯èƒ½éœ€è¦çœ‹ä¸€ä¸‹å®ƒç„æ‰‹å†Œé¡µă€‚ä¸è¿‡ -è®°ä½ï¼â€œindentâ€ä¸èƒ½ä¿®æ£åç„ç¼–ç¨‹ä¹ æƒ¯ă€‚ +“indent†有很å¤é€‰é¡¹ï¼Œç‰¹åˆ«æ˜¯é‡æ–°æ ¼å¼åŒ–注é‡ç„æ—¶å€™ï¼Œä½ å¯èƒ½éœ€è¦çœ‹ä¸€ä¸‹å®ƒç„æ‰‹å†Œé¡µă€‚ä¸è¿‡ +è®°ä½ï¼â€œindent†ä¸èƒ½ä¿®æ£åç„ç¼–ç¨‹ä¹ æƒ¯ă€‚ - 第åç« ï¼Kconfigé…置文件 + 第åç« ï¼Kconfig é…置文件 -对äºé布æºç æ ‘ç„æ‰€æœ‰Kconfig*é…置文件æ¥è¯´ï¼Œå®ƒä»¬ç¼©è¿›æ–¹å¼ä¸C代ç 相比有所ä¸åŒă€‚紧挨 -在“configâ€å®ä¹‰ä¸‹é¢ç„行缩进一个制表符,帮å©ä¿¡æ¯åˆ™å†å¤ç¼©è¿›2ä¸ªç©ºæ ¼ă€‚æ¯”å¦‚ï¼ +对äºé布æºç æ ‘ç„æ‰€æœ‰ Kconfig* é…置文件æ¥è¯´ï¼Œå®ƒä»¬ç¼©è¿›æ–¹å¼ä¸ C 代ç 相比有所ä¸åŒă€‚紧挨 +在 “config†å®ä¹‰ä¸‹é¢ç„行缩进一个制表符,帮å©ä¿¡æ¯åˆ™å†å¤ç¼©è¿› 2 ä¸ªç©ºæ ¼ă€‚æ¯”å¦‚ï¼ config AUDIT bool "Auditing support" @@ -470,7 +505,7 @@ config ADFS_FS_RW depends on ADFS_FS ... -è¦æŸ¥çœ‹é…置文件ç„完整文档,请看Documentation/kbuild/kconfig-language.txt。 +è¦æŸ¥çœ‹é…置文件ç„完整文档,请看 Documentation/kbuild/kconfig-language.txt。 第åä¸€ç« ï¼æ•°æ®ç»“æ„ @@ -489,11 +524,11 @@ config ADFS_FS_RW å¾ˆå¤æ•°æ®ç»“æ„å®é™…䏿œ‰2级引用计数,它们é€å¸¸æœ‰ä¸åŒâ€œç±»â€ç„ç”¨æˆ·ă€‚å类计数器统计å类用 æˆ·ç„æ•°é‡ï¼Œæ¯å½“å类计数器å‡è‡³é›¶æ—¶ï¼Œå…¨å±€è®¡æ•°å™¨å‡ä¸€ă€‚ -è¿™ç§â€œå¤çº§å¼•用计数â€ç„例åå¯ä»¥åœ¨å†…å˜ç®¡ç†ï¼ˆâ€œstruct mm_structâ€ï¼mm_userså’Œmm_count) +è¿™ç§â€œå¤çº§å¼•用计数â€ç„例åå¯ä»¥åœ¨å†…å˜ç®¡ç†ï¼ˆâ€œstruct mm_structâ€ï¼mm_users å’Œ mm_count) 和文件系统(“struct super_blockâ€ï¼s_countå’Œs_activeï¼‰ä¸æ‰¾åˆ°ă€‚ è®°ä½ï¼å¦‚æœå¦ä¸€ä¸ªæ‰§è¡Œçº¿ç´¢å¯ä»¥æ‰¾åˆ°ä½ ç„æ•°æ®ç»“æ„,但是这个数æ®ç»“æ„æ²¡æœ‰å¼•用计数器,这 -é‡Œå‡ ä¹è‚¯å®æ˜¯ä¸€ä¸ªbug。 +é‡Œå‡ ä¹è‚¯å®æ˜¯ä¸€ä¸ª bug。 第åäºŒç« ï¼å®ï¼Œæä¸¾å’ŒRTL @@ -508,102 +543,128 @@ config ADFS_FS_RW 一般ç„,如æœèƒ½å†™æˆå†…è”函数就ä¸è¦å†™æˆåƒå‡½æ•°ç„å®ă€‚ -嫿œ‰å¤ä¸ªè¯å¥ç„å®åº”该被包å«åœ¨ä¸€ä¸ªdo-while代ç å—é‡Œï¼ +嫿œ‰å¤ä¸ªè¯å¥ç„å®åº”该被包å«åœ¨ä¸€ä¸ª do-while 代ç å—é‡Œï¼ -#define macrofun(a, b, c) \ - do { \ - if (a == 5) \ - do_this(b, c); \ - } while (0) + #define macrofun(a, b, c) \ + do { \ + if (a == 5) \ + do_this(b, c); \ + } while (0) 使用å®ç„时候应é¿å…ç„äº‹æƒ…ï¼ 1) 影哿§åˆ¶æµç¨‹ç„å®ï¼ -#define FOO(x) \ - do { \ - if (blah(x) < 0) \ - return -EBUGGERED; \ - } while(0) + #define FOO(x) \ + do { \ + if (blah(x) < 0) \ + return -EBUGGERED; \ + } while (0) é常ä¸å¥½ă€‚它看起æ¥åƒä¸€ä¸ªå‡½æ•°ï¼Œä¸è¿‡å´èƒ½å¯¼è‡´â€œè°ƒç”¨â€å®ƒç„函数退出;ä¸è¦æ‰“乱读者大脑里 ç„è¯æ³•分æå™¨ă€‚ 2) ä¾èµ–äºä¸€ä¸ªå›ºå®åå—ç„æœ¬åœ°å˜é‡ç„å®ï¼ -#define FOO(val) bar(index, val) + #define FOO(val) bar(index, val) å¯èƒ½çœ‹èµ·æ¥åƒæ˜¯ä¸ªä¸é”™ç„东西,ä¸è¿‡å®ƒé常容易æè¯»ä»£ç ç„人æç³æ¶‚ï¼Œè€Œä¸”å®¹æ˜“å¯¼è‡´çœ‹èµ·æ¥ ä¸ç›¸å…³ç„改å¨å¸¦æ¥é”™è¯¯ă€‚ -3) 作为左值ç„另傿•°ç„å®ï¼ FOO(x) = yï¼›å¦‚æœæœ‰äººæFOOå˜æˆä¸€ä¸ªå†…è”函数ç„è¯ï¼Œè¿™ç§ç”¨ +3) 作为左值ç„另傿•°ç„å®ï¼ FOO(x) = yï¼›å¦‚æœæœ‰äººæ FOO å˜æˆä¸€ä¸ªå†…è”函数ç„è¯ï¼Œè¿™ç§ç”¨ 法就ä¼å‡ºé”™äº†ă€‚ 4) 忘记了优先级ï¼ä½¿ç”¨è¡¨è¾¾å¼å®ä¹‰å¸¸é‡ç„å®å¿…须将表达å¼ç½®äºä¸€å¯¹å°æ‹¬å·ä¹‹å†…ă€‚å¸¦å‚æ•°ç„ å®ä¹Ÿè¦æ³¨æ„æ¤ç±»é—®é¢˜ă€‚ -#define CONSTANT 0x4000 -#define CONSTEXP (CONSTANT | 3) + #define CONSTANT 0x4000 + #define CONSTEXP (CONSTANT | 3) + +5) 在å®é‡Œå®ä¹‰ç±»ä¼¼å‡½æ•°ç„本地å˜é‡æ—¶å‘½å冲çªï¼ -cpp手册对å®ç„è®²è§£å¾ˆè¯¦ç»†ă€‚Gcc internals手册也详细讲解了RTL(译注ï¼register + #define FOO(x) \ + ({ \ + typeof(x) ret; \ + ret = calc_ret(x); \ + (ret); \ + }) + +ret 是本地å˜é‡ç„é€ç”¨åå— - __foo_ret æ›´ä¸å®¹æ˜“ä¸ä¸€ä¸ªå·²å˜åœ¨ç„å˜é‡å†²çªă€‚ + +cpp 手册对å®ç„è®²è§£å¾ˆè¯¦ç»†ă€‚gcc internals 手册也详细讲解了 RTL(译注ï¼register transfer languageï¼‰ï¼Œå†…æ ¸é‡Œç„æ±‡ç¼–è¯è¨€ç»å¸¸ç”¨åˆ°å®ƒă€‚ 第åä¸‰ç« ï¼æ‰“å°å†…æ ¸æ¶ˆæ¯ å†…æ ¸å¼€å‘者应该是å—过良好教育ç„ă€‚è¯·ä¸€å®æ³¨æ„å†…æ ¸ä¿¡æ¯ç„拼写,以给人以好ç„å°è±¡ă€‚ä¸è¦ -用ä¸è§„范ç„å•è¯æ¯”如“dontâ€ï¼Œè€Œè¦ç”¨â€œdo notâ€æˆ–者“don'tâ€ă€‚ä¿è¯è¿™äº›ä¿¡æ¯ç®€å•ă€æ˜äº†ă€æ— -æ§ä¹‰ă€‚ +用ä¸è§„范ç„å•è¯æ¯”如 “dontâ€ï¼Œè€Œè¦ç”¨ “do notâ€æˆ–者 “don'tâ€ă€‚ä¿è¯è¿™äº›ä¿¡æ¯ç®€å•ă€æ˜äº†ă€ +æ— æ§ä¹‰ă€‚ å†…æ ¸ä¿¡æ¯ä¸å¿…以å¥å·ï¼ˆè¯‘注ï¼è‹±æ–‡å¥å·ï¼Œå³ç‚¹ï¼‰ç»“æŸă€‚ -åœ¨å°æ‹¬å·é‡Œæ‰“å°æ•°å—(%d)没有任何价值,应该é¿å…è¿™æ ·å。 +åœ¨å°æ‹¬å·é‡Œæ‰“å°æ•°å— (%d) 没有任何价值,应该é¿å…è¿™æ ·å。 -<linux/device.h>é‡Œæœ‰ä¸€äº›é©±å¨æ¨¡å‹è¯æ–å®ï¼Œä½ 应该使用它们,以确ä¿ä¿¡æ¯å¯¹åº”äºæ£ç¡®ç„ -设备和驱å¨ï¼Œå¹¶ä¸”è¢«æ ‡è®°äº†æ£ç¡®ç„消æ¯çº§åˆ«ă€‚è¿™äº›å®æœ‰ï¼dev_err(), dev_warn(), -dev_info()ç‰ç‰ă€‚对äºé‚£äº›ä¸å’ŒæŸä¸ªç‰¹å®è®¾å¤‡ç›¸å…³è¿ç„ä¿¡æ¯ï¼Œ<linux/kernel.h>å®ä¹‰äº† -pr_debug()å’Œpr_info()。 +<linux/device.h> é‡Œæœ‰ä¸€äº›é©±å¨æ¨¡å‹è¯æ–å®ï¼Œä½ 应该使用它们,以确ä¿ä¿¡æ¯å¯¹åº”äºæ£ç¡®ç„ +设备和驱å¨ï¼Œå¹¶ä¸”è¢«æ ‡è®°äº†æ£ç¡®ç„消æ¯çº§åˆ«ă€‚è¿™äº›å®æœ‰ï¼dev_err(),dev_warn(), +dev_info() ç‰ç‰ă€‚对äºé‚£äº›ä¸å’ŒæŸä¸ªç‰¹å®è®¾å¤‡ç›¸å…³è¿ç„ä¿¡æ¯ï¼Œ<linux/printk.h> å®ä¹‰äº† +pr_notice(),pr_info(),pr_warn(),pr_err() å’Œå…¶ä»–ă€‚ -写出好ç„调试信æ¯å¯ä»¥æ˜¯ä¸€ä¸ªå¾ˆå¤§ç„æŒ‘æˆ˜ï¼›å½“ä½ å†™å‡ºæ¥ä¹‹å,这些信æ¯åœ¨è¿œç¨‹é™¤é”™ç„时候 -就伿ˆä¸ºæå¤§ç„帮å©ă€‚当DEBUGç¬¦å·æ²¡æœ‰è¢«å®ä¹‰ç„时候,这些信æ¯ä¸åº”è¯¥è¢«ç¼–è¯‘è¿›å†…æ ¸é‡Œ -(也就是说,默认地,它们ä¸åº”该被包å«åœ¨å†…ï¼‰ă€‚å¦‚æœä½ 使用dev_dbg()或者pr_debug(), -就能自å¨è¾¾åˆ°è¿™ä¸ªæ•ˆæœă€‚很å¤å系统拥有Kconfig选项æ¥å¯ç”¨-DDEBUGă€‚è¿˜æœ‰ä¸€ä¸ªç›¸å…³ç„æƒ¯ä¾‹ -是使用VERBOSE_DEBUGæ¥æ·»å dev_vdbg()消æ¯åˆ°é‚£äº›å·²ç»ç”±DEBUGå¯ç”¨ç„消æ¯ä¹‹ä¸ă€‚ +写出好ç„调试信æ¯å¯ä»¥æ˜¯ä¸€ä¸ªå¾ˆå¤§ç„æŒ‘æˆ˜ï¼›ä¸€æ—¦ä½ å†™å‡ºå,这些信æ¯åœ¨è¿œç¨‹é™¤é”™æ—¶èƒ½æä¾›æå¤§ +ç„帮å©ă€‚然而打å°è°ƒè¯•ä¿¡æ¯ç„å¤„ç†æ–¹å¼åŒæ‰“å°é调试信æ¯ä¸åŒă€‚å…¶ä»– pr_XXX() å‡½æ•°èƒ½æ— æ¡ä»¶åœ° +打å°ï¼Œpr_debug() å´ä¸ï¼›é»˜è®¤æƒ…况下它ä¸ä¼è¢«ç¼–译,除éå®ä¹‰äº† DEBUG 或设å®äº† +CONFIG_DYNAMIC_DEBUG。å®é™…è¿™åŒæ ·æ˜¯ä¸ºäº† dev_dbg()ï¼Œä¸€ä¸ªç›¸å…³çº¦å®æ˜¯åœ¨ä¸€ä¸ªå·²ç»å¼€å¯äº† +DEBUG 时,使用 VERBOSE_DEBUG æ¥æ·»å dev_vdbg()。 + +许å¤å系统拥有 Kconfig 调试选项æ¥å¼€å¯ -DDEBUG åœ¨å¯¹åº”ç„ Makefile 里é¢ï¼›åœ¨å…¶ä»– +æƒ…å†µä¸‹ï¼Œç‰¹æ®æ–‡ä»¶ä½¿ç”¨ #define DEBUGă€‚å½“ä¸€æ¡è°ƒè¯•ä¿¡æ¯éœ€è¦è¢«æ— æ¡ä»¶æ‰“å°æ—¶ï¼Œä¾‹å¦‚ï¼Œå¦‚æœ +å·²ç»åŒ…å«ä¸€ä¸ªè°ƒè¯•ç›¸å…³ç„ #ifdef æ¡ä»¶ï¼Œprintk(KERN_DEBUG ...) å°±å¯è¢«ä½¿ç”¨ă€‚ 第åå››ç« ï¼åˆ†é…å†…å˜ -å†…æ ¸æä¾›äº†ä¸‹é¢ç„一般用途ç„内å˜åˆ†é…函数ï¼kmalloc(),kzalloc(),kcalloc()å’Œ -vmalloc()ă€‚è¯·å‚考API文档以è·å–有关它们ç„详细信æ¯ă€‚ +å†…æ ¸æä¾›äº†ä¸‹é¢ç„一般用途ç„内å˜åˆ†é…å‡½æ•°ï¼ +kmalloc(),kzalloc(),kmalloc_array(),kcalloc(),vmalloc() å’Œ vzalloc()。 +请å‚考 API 文档以è·å–有关它们ç„详细信æ¯ă€‚ ä¼ é€’ç»“æ„体大å°ç„首选形弿˜¯è¿™æ ·ç„ï¼ p = kmalloc(sizeof(*p), ...); -å¦å¤–一ç§ä¼ 递方å¼ä¸ï¼Œsizeofç„æ“作数是结æ„体ç„åå—ï¼Œè¿™æ ·ä¼é™ä½å¯è¯»æ€§ï¼Œå¹¶ä¸”å¯èƒ½ä¼å¼• -å…¥bugă€‚æœ‰å¯èƒ½æŒ‡é’ˆå˜é‡ç±»å‹è¢«æ”¹å˜æ—¶ï¼Œè€Œå¯¹åº”ç„ä¼ é€’ç»™å†…å˜åˆ†é…函数ç„sizeofç„结æœä¸å˜ă€‚ +å¦å¤–一ç§ä¼ 递方å¼ä¸ï¼Œsizeof ç„æ“作数是结æ„体ç„åå—ï¼Œè¿™æ ·ä¼é™ä½å¯è¯»æ€§ï¼Œå¹¶ä¸”å¯èƒ½ä¼å¼• +å…¥ bugă€‚æœ‰å¯èƒ½æŒ‡é’ˆå˜é‡ç±»å‹è¢«æ”¹å˜æ—¶ï¼Œè€Œå¯¹åº”ç„ä¼ é€’ç»™å†…å˜åˆ†é…å‡½æ•°ç„ sizeof ç„结æœä¸å˜ă€‚ -强制转æ¢ä¸€ä¸ªvoid指针返å›å€¼æ˜¯å¤ä½™ç„。Cè¯è¨€æœ¬èº«ä¿è¯äº†ä»voidæŒ‡é’ˆåˆ°å…¶ä»–ä»»ä½•æŒ‡é’ˆç±»å‹ +强制转æ¢ä¸€ä¸ª void 指针返å›å€¼æ˜¯å¤ä½™ç„。C è¯è¨€æœ¬èº«ä¿è¯äº†ä» void æŒ‡é’ˆåˆ°å…¶ä»–ä»»ä½•æŒ‡é’ˆç±»å‹ ç„è½¬æ¢æ˜¯æ²¡æœ‰é—®é¢˜ç„。 +分é…一个数组ç„首选形弿˜¯è¿™æ ·ç„ï¼ + + p = kmalloc_array(n, sizeof(...), ...); + +分é…一个零长数组ç„首选形弿˜¯è¿™æ ·ç„ï¼ + + p = kcalloc(n, sizeof(...), ...); + +两ç§å½¢å¼æ£€æŸ¥åˆ†é…å¤§å° n * sizeof(...) ç„æº¢å‡ºï¼Œå¦‚æœæº¢å‡ºè¿”å› NULL。 + 第åäº”ç« ï¼å†…è”å¼ç—… -有一个常è§ç„误解是内è”函数是gccæä¾›ç„å¯ä»¥è®©ä»£ç è¿è¡Œæ›´å¿«ç„ä¸€ä¸ªé€‰é¡¹ă€‚è™½ç„¶ä½¿ç”¨å†…è” +有一个常è§ç„误解是内è”函数是 gcc æä¾›ç„å¯ä»¥è®©ä»£ç è¿è¡Œæ›´å¿«ç„ä¸€ä¸ªé€‰é¡¹ă€‚è™½ç„¶ä½¿ç”¨å†…è” å‡½æ•°æœ‰æ—¶å€™æ˜¯æ°å½“ç„ï¼ˆæ¯”å¦‚ä½œä¸ºä¸€ç§æ›¿ä»£å®ç„æ–¹å¼ï¼Œè¯·çœ‹ç¬¬åäºŒç« ï¼‰ï¼Œä¸è¿‡å¾ˆå¤æƒ…况䏋䏿˜¯ -è¿™æ ·ă€‚inline关键å—ç„过度使用ä¼ä½¿å†…æ ¸å˜å¤§ï¼Œä»è€Œä½¿æ•´ä¸ªç³»ç»Ÿè¿è¡Œé€Ÿåº¦å˜æ…¢ă€‚å› ä¸ºå¤§å†…æ ¸ +è¿™æ ·ă€‚inline 关键å—ç„过度使用ä¼ä½¿å†…æ ¸å˜å¤§ï¼Œä»è€Œä½¿æ•´ä¸ªç³»ç»Ÿè¿è¡Œé€Ÿåº¦å˜æ…¢ă€‚å› ä¸ºå¤§å†…æ ¸ ä¼å 用更å¤ç„指令高速缓å˜ï¼ˆè¯‘注ï¼ä¸€çº§ç¼“å˜é€å¸¸æ˜¯æŒ‡ä»¤ç¼“å˜å’Œæ•°æ®ç¼“å˜åˆ†å¼€ç„)而且ä¼å¯¼ -致pagecacheç„å¯ç”¨å†…å˜å‡å°‘ă€‚æƒ³è±¡ä¸€ä¸‹ï¼Œä¸€æ¬¡pagecache未命ä¸å°±ä¼å¯¼è‡´ä¸€æ¬¡ç£ç›˜å¯»å€ï¼Œå°† -耗时5æ¯«ç§’ă€‚5æ¯«ç§’ç„æ—¶é—´å†…CPU能执行很å¤å¾ˆå¤æŒ‡ä»¤ă€‚ +致 pagecache ç„å¯ç”¨å†…å˜å‡å°‘ă€‚æƒ³è±¡ä¸€ä¸‹ï¼Œä¸€æ¬¡pagecache未命ä¸å°±ä¼å¯¼è‡´ä¸€æ¬¡ç£ç›˜å¯»å€ï¼Œ +将耗时 5 æ¯«ç§’ă€‚5 æ¯«ç§’ç„æ—¶é—´å†… CPU 能执行很å¤å¾ˆå¤æŒ‡ä»¤ă€‚ -一个基本ç„åŸåˆ™æ˜¯å¦‚æœä¸€ä¸ªå‡½æ•°æœ‰3行以ä¸ï¼Œå°±ä¸è¦æå®ƒå˜æˆå†…è”å‡½æ•°ă€‚è¿™ä¸ªåŸåˆ™ç„一个例 +一个基本ç„åŸåˆ™æ˜¯å¦‚æœä¸€ä¸ªå‡½æ•°æœ‰ 3 行以ä¸ï¼Œå°±ä¸è¦æå®ƒå˜æˆå†…è”å‡½æ•°ă€‚è¿™ä¸ªåŸåˆ™ç„一个例 外是,如æœä½ çŸ¥é“æŸä¸ªå‚数是一个编译时常é‡ï¼Œè€Œä¸”å› ä¸ºè¿™ä¸ªå¸¸é‡ä½ ç¡®å®ç¼–译器在编译时能 -优化æ‰ä½ ç„函数ç„大部分代ç ,那ä»ç„¶å¯ä»¥ç»™å®ƒå ä¸inline关键å—。kmalloc()内è”函数就 +优化æ‰ä½ ç„函数ç„大部分代ç ,那ä»ç„¶å¯ä»¥ç»™å®ƒå ä¸ inline 关键å—。kmalloc() 内è”函数就 是一个很好ç„例å。 -人们ç»å¸¸ä¸»å¼ ç»™staticç„而且åªç”¨äº†ä¸€æ¬¡ç„函数å ä¸inline,如æ¤ä¸ä¼æœ‰ä»»ä½•æŸå¤±ï¼Œå› 为没 -有什么好æƒè¡¡ç„ă€‚è™½ç„¶ä»æ€æœ¯ä¸è¯´è¿™æ˜¯æ£ç¡®ç„,但是å®é™…ä¸è¿™ç§æƒ…况下å³ä½¿ä¸å inline gcc -也å¯ä»¥è‡ªå¨ä½¿å…¶å†…è”ă€‚è€Œä¸”å…¶ä»–ç”¨æˆ·å¯èƒ½ä¼è¦æ±‚移除inline,由æ¤è€Œæ¥ç„äº‰è®ºä¼æµæ¶ˆinline +人们ç»å¸¸ä¸»å¼ ç»™ static ç„而且åªç”¨äº†ä¸€æ¬¡ç„函数å ä¸ inline,如æ¤ä¸ä¼æœ‰ä»»ä½•æŸå¤±ï¼Œå› 为没 +有什么好æƒè¡¡ç„ă€‚è™½ç„¶ä»æ€æœ¯ä¸è¯´è¿™æ˜¯æ£ç¡®ç„,但是å®é™…ä¸è¿™ç§æƒ…况下å³ä½¿ä¸å inline gcc +也å¯ä»¥è‡ªå¨ä½¿å…¶å†…è”ă€‚è€Œä¸”å…¶ä»–ç”¨æˆ·å¯èƒ½ä¼è¦æ±‚移除 inline,由æ¤è€Œæ¥ç„äº‰è®ºä¼æµæ¶ˆ inline è‡ªèº«ç„æ½œåœ¨ä»·å€¼ï¼Œå¾—ä¸å¿å¤±ă€‚ @@ -613,37 +674,37 @@ vmalloc()ă€‚è¯·å‚考API文档以è·å–有关它们ç„详细信æ¯ă€‚ ç„一个值å¯ä»¥è¡¨ç¤ºä¸ºä¸€ä¸ªé”™è¯¯ä»£ç 整数(-Exxxï¼å¤±è´¥ï¼Œ0ï¼æˆåŸï¼‰æˆ–者一个“æˆåŸâ€å¸ƒå°”值( 0ï¼å¤±è´¥ï¼Œé0ï¼æˆåŸï¼‰ă€‚ -æ··åˆä½¿ç”¨è¿™ä¸¤ç§è¡¨è¾¾æ–¹å¼æ˜¯é¾äºå‘ç°ç„bugç„æ¥æºă€‚如æœCè¯è¨€æœ¬èº«ä¸¥æ ¼åŒºåˆ†æ•´å½¢å’Œå¸ƒå°”å‹å˜ -é‡ï¼Œé‚£ä¹ˆç¼–译器就能够帮我们å‘ç°è¿™äº›é”™è¯¯â€¦â€¦ä¸è¿‡Cè¯è¨€ä¸åŒºåˆ†ă€‚为了é¿å…产生这ç§bug,请 +æ··åˆä½¿ç”¨è¿™ä¸¤ç§è¡¨è¾¾æ–¹å¼æ˜¯é¾äºå‘ç°ç„ bug ç„æ¥æºă€‚å¦‚æœ C è¯è¨€æœ¬èº«ä¸¥æ ¼åŒºåˆ†æ•´å½¢å’Œå¸ƒå°”å‹å˜ +é‡ï¼Œé‚£ä¹ˆç¼–译器就能够帮我们å‘ç°è¿™äº›é”™è¯¯â€¦â€¦ä¸è¿‡ C è¯è¨€ä¸åŒºåˆ†ă€‚为了é¿å…äº§ç”Ÿè¿™ç§ bug,请 éµå¾ªä¸‹é¢ç„æƒ¯ä¾‹ï¼ å¦‚æœå‡½æ•°ç„åå—æ˜¯ä¸€ä¸ªå¨ä½œæˆ–者强制性ç„命令,那么这个函数应该返å›é”™è¯¯ä»£ç æ•´ æ•°ă€‚å¦‚æœæ˜¯ä¸€ä¸ªåˆ¤æ–,那么函数应该返å›ä¸€ä¸ªâ€œæˆåŸâ€å¸ƒå°”å€¼ă€‚ -比如,“add workâ€æ˜¯ä¸€ä¸ªå‘½ä»¤ï¼Œæ‰€ä»¥add_work()函数在æˆåŸæ—¶è¿”å›0,在失败时返å›-EBUSY。 -类似ç„ï¼Œå› ä¸ºâ€œPCI device presentâ€æ˜¯ä¸€ä¸ªåˆ¤æ–,所以pci_dev_present()函数在æˆåŸæ‰¾åˆ° -一个匹é…ç„设备时应该返å›1ï¼Œå¦‚æœæ‰¾ä¸åˆ°æ—¶åº”该返å›0。 +比如,“add work†是一个命令,所以 add_work() 函数在æˆåŸæ—¶è¿”å› 0ï¼Œåœ¨å¤±è´¥æ—¶è¿”å› -EBUSY。 +类似ç„ï¼Œå› ä¸º “PCI device present†是一个判æ–,所以 pci_dev_present() 函数在æˆåŸæ‰¾åˆ° +一个匹é…ç„è®¾å¤‡æ—¶åº”è¯¥è¿”å› 1ï¼Œå¦‚æœæ‰¾ä¸åˆ°æ—¶åº”è¯¥è¿”å› 0。 所有导出(译注ï¼EXPORT)ç„函数都必须éµå®ˆè¿™ä¸ªæƒ¯ä¾‹ï¼Œæ‰€æœ‰ç„公共函数也都应该如æ¤ă€‚ç§ æœ‰ï¼ˆstatic)函数ä¸éœ€è¦å¦‚æ¤ï¼Œä½†æ˜¯æˆ‘们也æ¨èè¿™æ ·å。 è¿”å›å€¼æ˜¯å®é™…计算结æœè€Œä¸æ˜¯è®¡ç®—æ˜¯å¦æˆåŸç„æ ‡å¿—ç„函数ä¸å—æ¤æƒ¯ä¾‹ç„é™åˆ¶ă€‚一般ç„,他们 é€è¿‡è¿”å›ä¸€äº›æ£å¸¸å€¼èŒƒå›´ä¹‹å¤–ç„ç»“æœæ¥è¡¨ç¤ºå‡ºé”™ă€‚å…¸å‹ç„ä¾‹åæ˜¯è¿”囿Œ‡é’ˆç„函数,他们使用 -NULL或者ERR_PTRæœºåˆ¶æ¥æ¥å‘é”™è¯¯ă€‚ +NULL 或者 ERR_PTR æœºåˆ¶æ¥æ¥å‘é”™è¯¯ă€‚ 第åä¸ƒç« ï¼ä¸è¦é‡æ–°å‘æ˜å†…æ ¸å® -头文件include/linux/kernel.h包å«äº†ä¸€äº›å®ï¼Œä½ 应该使用它们,而ä¸è¦è‡ªå·±å†™ä¸€äº›å®ƒä»¬ç„ +头文件 include/linux/kernel.h 包å«äº†ä¸€äº›å®ï¼Œä½ 应该使用它们,而ä¸è¦è‡ªå·±å†™ä¸€äº›å®ƒä»¬ç„ å˜ç§ă€‚比如,如æœä½ 需è¦è®¡ç®—一个数组ç„é•¿åº¦ï¼Œä½¿ç”¨è¿™ä¸ªå® - #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 类似ç„,如æœä½ è¦è®¡ç®—æŸç»“æ„体æˆå‘˜ç„大å°ï¼Œä½¿ç”¨ - #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) + #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) -还有å¯ä»¥åä¸¥æ ¼ç„ç±»å‹æ£€æŸ¥ç„min()å’Œmax()å®ï¼Œå¦‚æœä½ 需è¦å¯ä»¥ä½¿ç”¨å®ƒä»¬ă€‚ä½ å¯ä»¥è‡ªå·±çœ‹çœ‹ +还有å¯ä»¥åä¸¥æ ¼ç„ç±»å‹æ£€æŸ¥ç„ min() å’Œ max() å®ï¼Œå¦‚æœä½ 需è¦å¯ä»¥ä½¿ç”¨å®ƒä»¬ă€‚ä½ å¯ä»¥è‡ªå·±çœ‹çœ‹ 那个头文件里还å®ä¹‰äº†ä»€ä¹ˆä½ å¯ä»¥æ‹¿æ¥ç”¨ç„ä¸œè¥¿ï¼Œå¦‚æœæœ‰å®ä¹‰ç„è¯ï¼Œä½ å°±ä¸åº”åœ¨ä½ ç„代ç 里 è‡ªå·±é‡æ–°å®ä¹‰ă€‚ @@ -653,42 +714,100 @@ NULL或者ERR_PTRæœºåˆ¶æ¥æ¥å‘é”™è¯¯ă€‚ 有一些编辑器å¯ä»¥è§£é‡åµŒå…¥åœ¨æºæ–‡ä»¶é‡Œç„ç”±ä¸€äº›ç‰¹æ®æ ‡è®°æ ‡æ˜ç„é…置信æ¯ă€‚比如,emacs 能够解é‡è¢«æ ‡è®°æˆè¿™æ ·ç„è¡Œï¼ --*- mode: c -*- + -*- mode: c -*- æˆ–è€…è¿™æ ·ç„ï¼ -/* -Local Variables: -compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c" -End: -*/ + /* + Local Variables: + compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c" + End: + */ -Vim能够解é‡è¿™æ ·ç„æ ‡è®°ï¼ +Vim 能够解é‡è¿™æ ·ç„æ ‡è®°ï¼ -/* vim:set sw=8 noet */ + /* vim:set sw=8 noet */ ä¸è¦åœ¨æºä»£ç ä¸åŒ…å«ä»»ä½•è¿™æ ·ç„å†…å®¹ă€‚æ¯ä¸ªäººéƒ½æœ‰ä»–自己ç„编辑器é…ç½®ï¼Œä½ ç„æºæ–‡ä»¶ä¸åº” 该覆盖别人ç„é…ç½®ă€‚è¿™åŒ…æ‹¬æœ‰å…³ç¼©è¿›å’Œæ¨¡å¼é…ç½®ç„æ ‡è®°ă€‚人们å¯ä»¥ä½¿ç”¨ä»–们自己å®åˆ¶ç„模 å¼ï¼Œæˆ–者使用其他å¯ä»¥äº§ç”Ÿæ£ç¡®ç„缩进ç„å·§å¦™æ–¹æ³•ă€‚ + 第åä¹ç« ï¼å†…è”æ±‡ç¼– + +åœ¨ç‰¹å®æ¶æ„ç„代ç ä¸ï¼Œä½ 也许需è¦å†…è”æ±‡ç¼–æ¥ä½¿ç”¨ CPU æ¥å£å’Œå¹³å°ç›¸å…³åŸèƒ½ă€‚åœ¨éœ€è¦ +è¿™ä¹ˆåæ—¶ï¼Œä¸è¦ç¹è±«ă€‚然而,当 C å¯ä»¥å®Œæˆå·¥ä½œæ—¶ï¼Œä¸è¦æ— ç«¯åœ°ä½¿ç”¨å†…è”æ±‡ç¼–ă€‚å¦‚æœ +å¯èƒ½ï¼Œä½ å¯ä»¥å¹¶ä¸”应该用 C å’Œç¡¬ä»¶äº¤äº’ă€‚ + +考虑å»å†™é€ç”¨ä¸€ç‚¹ç„å†…è”æ±‡ç¼–作为简æ˜ç„è¾…å©å‡½æ•°ï¼Œè€Œä¸æ˜¯é‡å¤å†™ä¸‹å®ƒä»¬ç„细è‚ă€‚è®°ä½ +å†…è”æ±‡ç¼–å¯ä»¥ä½¿ç”¨ C 傿•°ă€‚ + +大而特æ®ç„汇编函数应该放在 .S 文件ä¸ï¼Œå¯¹åº” C ç„åŸå‹å®ä¹‰åœ¨ C 头文件ä¸ă€‚汇编 +å‡½æ•°ç„ C åŸå‹åº”该使用 “asmlinkageâ€ă€‚ + +ä½ å¯èƒ½éœ€è¦å°†ä½ ç„æ±‡ç¼–è¯å¥æ ‡è®°ä¸º volatile,æ¥é˜»æ¢ GCC 在没å‘ç°ä»»ä½•副作用åå°± +ç§»é™¤äº†å®ƒă€‚ä½ ä¸å¿…æ€»æ˜¯è¿™æ ·åï¼Œè™½ç„¶ï¼Œè¿™æ ·å¯ä»¥é™åˆ¶ä¸å¿…è¦ç„ä¼˜åŒ–ă€‚ + +在写一个包å«å¤æ¡æŒ‡ä»¤ç„å•ä¸ªå†…è”æ±‡ç¼–è¯å¥æ—¶ï¼Œææ¯æ¡æŒ‡ä»¤ç”¨å¼•å·å—符串分离,并写在 +å•独一行,在æ¯ä¸ªå—符串结尾,除了 \n\t 结尾之外,在汇编输出ä¸é€‚当地缩进下 +ä¸€æ¡æŒ‡ä»¤ï¼ + + asm ("magic %reg1, #42\n\t" + "more_magic %reg2, %reg3" + : /* outputs */ : /* inputs */ : /* clobbers */); + + + 第二åç« ï¼æ¡ä»¶ç¼–译 + +åªè¦å¯èƒ½ï¼Œå°±ä¸è¦åœ¨ .c 文件里é¢ä½¿ç”¨é¢„å¤„ç†æ¡ä»¶ï¼›è¿™æ ·åè®©ä»£ç æ›´é¾é˜…读并且逻辑é¾ä»¥ +è·Ÿè¸ªă€‚æ›¿ä»£æ–¹æ¡ˆæ˜¯ï¼Œåœ¨å¤´æ–‡ä»¶å®ä¹‰å‡½æ•°åœ¨è¿™äº› .c 文件ä¸ä½¿ç”¨è¿™ç±»ç„æ¡ä»¶è¡¨è¾¾å¼ï¼Œæä¾›ç©º +æ“ä½œç„æ¡©ç‰ˆæœ¬ï¼ˆè¯‘æ³¨ï¼æ¡©ç¨‹åºï¼Œæ˜¯æŒ‡ç”¨æ¥æ›¿æ¢ä¸€éƒ¨åˆ†åŸèƒ½ç„ç¨‹åºæ®µï¼‰åœ¨ #else 情况下, +å†ä» .c æ–‡ä»¶ä¸æ— æ¡ä»¶åœ°è°ƒç”¨è¿™äº›å‡½æ•°ă€‚编译器ä¼é¿å…生æˆä»»ä½•桩调用ç„代ç ,产生一致 +ç„结æœï¼Œä½†é€»è¾‘å°†æ›´å æ¸…æ™°ă€‚ + +å®å¯ç¼–è¯‘æ•´ä¸ªå‡½æ•°ï¼Œè€Œä¸æ˜¯éƒ¨åˆ†å‡½æ•°æˆ–部分表达å¼ă€‚è€Œä¸æ˜¯åœ¨ä¸€ä¸ªè¡¨è¾¾å¼æ·»å ifdef, +è§£æéƒ¨åˆ†æˆ–全部表达å¼åˆ°ä¸€ä¸ªå•独ç„è¾…å©å‡½æ•°ï¼Œå¹¶åº”用æ¡ä»¶åˆ°è¯¥å‡½æ•°å†…。 + +如æœä½ 有一个在特å®é…ç½®ä¸å¯èƒ½æ˜¯æœªä½¿ç”¨ç„函数或å˜é‡ï¼Œç¼–译器将è¦å‘它å®ä¹‰äº†ä½†æœªä½¿ç”¨ï¼Œ +æ ‡è®°è¿™ä¸ªå®ä¹‰ä¸º __maybe_unused è€Œä¸æ˜¯å°†å®ƒåŒ…å«åœ¨ä¸€ä¸ªé¢„å¤„ç†æ¡ä»¶ä¸ă€‚ï¼ˆç„¶è€Œï¼Œå¦‚æœ +一个函数或å˜é‡æ€»æ˜¯æœªä½¿ç”¨ç„,就直æ¥åˆ é™¤å®ƒă€‚ï¼‰ + +在代ç ä¸ï¼Œå¯èƒ½ç„情况下,使用 IS_ENABLED 宿¥è½¬åŒ–æŸä¸ª Kconfig æ ‡è®°ä¸º C ç„布尔 +表达å¼ï¼Œå¹¶åœ¨æ£å¸¸ç„ C æ¡ä»¶ä¸ä½¿ç”¨å®ƒï¼ + + if (IS_ENABLED(CONFIG_SOMETHING)) { + ... + } + +ç¼–è¯‘å™¨ä¼æ— æ¡ä»¶åœ°å常数åˆå¹¶ï¼Œå°±åƒä½¿ç”¨ #ifdef é‚£æ ·ï¼ŒåŒ…å«æˆ–æ’除代ç å—,所以这ä¸ä¼ +带æ¥ä»»ä½•è¿è¡Œæ—¶å¼€é”€ă€‚ç„¶è€Œï¼Œè¿™ç§æ–¹æ³•便—§å…许 C 编译器查看å—内ç„代ç ï¼Œå¹¶æ£€æŸ¥å®ƒç„æ£ç¡® +æ€§ï¼ˆè¯æ³•,类å‹ï¼Œç¬¦å·å¼•用,ç‰ç‰ï¼‰ă€‚å› æ¤ï¼Œå¦‚æœæ¡ä»¶ä¸æ»¡è¶³ï¼Œä»£ç å—内ç„引用符å·å°†ä¸å˜åœ¨ï¼Œ +ä½ å¿…é¡»ç»§ç»ä½¿ç”¨ #ifdef。 + +在任何有æ„ä¹‰ç„ #if 或 #ifdef å—ç„æœ«å°¾ï¼ˆè¶…è¿‡å‡ è¡Œï¼‰ï¼Œåœ¨ #endif åŒä¸€è¡Œç„åé¢å†™ä¸‹ +注é‡ï¼ŒæŒ‡å‡ºè¯¥æ¡ä»¶è¡¨è¾¾å¼è¢«ä½¿ç”¨ă€‚ä¾‹å¦‚ï¼ + + #ifdef CONFIG_SOMETHING + ... + #endif /* CONFIG_SOMETHING */ + 附录 Iï¼å‚考 -The C Programming Language, 第二版, 作者Brian W. Kernighanå’ŒDenni -M. Ritchie. Prentice Hall, Inc., 1988. ISBN 0-13-110362-8 (软ç®), -0-13-110370-9 (硬ç®). URL: http://cm.bell-labs.com/cm/cs/cbook/ +The C Programming Language, 第二版 +作者ï¼Brian W. Kernighan å’Œ Denni M. Ritchie. +Prentice Hall, Inc., 1988. +ISBN 0-13-110362-8 (软ç®), 0-13-110370-9 (硬ç®). -The Practice of Programming 作者Brian W. Kernighanå’ŒRob Pike. Addison-Wesley, -Inc., 1999. ISBN 0-201-61586-X. URL: http://cm.bell-labs.com/cm/cs/tpop/ +The Practice of Programming +作者ï¼Brian W. Kernighan å’Œ Rob Pike. +Addison-Wesley, Inc., 1999. +ISBN 0-201-61586-X. -cpp,gcc,gcc internalså’Œindentç„GNU手册——和K&Råæœ¬æ–‡ç›¸ç¬¦åˆç„部分,全部å¯ä»¥åœ¨ -http://www.gnu.org/manual/找到 +GNU 手册 - éµå¾ª K&R æ ‡å‡†å’Œæ¤æ–‡æœ¬ - cpp, gcc, gcc internals and indent, +都å¯ä»¥ä» http://www.gnu.org/manual/ 找到 WG14是Cè¯è¨€ç„å›½é™…æ ‡å‡†åŒ–å·¥ä½œç»„ï¼ŒURL: http://www.open-std.org/JTC1/SC22/WG14/ -Kernel CodingStyle,作者greg@kroah.comå‘表äºOLS 2002ï¼ +Kernel CodingStyle,作者 greg@kroah.com å‘表äºOLS 2002ï¼ http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/ - --- -æœ€åæ›´æ–°äº2007å¹´7月13æ—¥ă€‚ @@ -1412,8 +1412,11 @@ $(help-board-dirs): help-%: # Documentation targets # --------------------------------------------------------------------------- -%docs: scripts_basic FORCE +DOC_TARGETS := xmldocs sgmldocs psdocs pdfdocs htmldocs mandocs installmandocs epubdocs cleandocs +PHONY += $(DOC_TARGETS) +$(DOC_TARGETS): scripts_basic FORCE $(Q)$(MAKE) $(build)=scripts build_docproc build_check-lc_ctype + $(Q)$(MAKE) $(build)=Documentation -f $(srctree)/Documentation/Makefile.sphinx $@ $(Q)$(MAKE) $(build)=Documentation/DocBook $@ else # KBUILD_EXTMOD diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index be43afb08c69..e3dba6f44a79 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -8,7 +8,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_lock.o drm_memory.o drm_drv.o drm_vm.o \ drm_scatter.o drm_pci.o \ drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ - drm_crtc.o drm_modes.o drm_edid.o \ + drm_crtc.o drm_fourcc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ drm_trace_points.o drm_global.o drm_prime.o \ drm_rect.o drm_vma_manager.o drm_flip_work.o \ @@ -23,7 +23,8 @@ drm-$(CONFIG_AGP) += drm_agpsupport.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_kms_helper_common.o drm_dp_dual_mode_helper.o \ + drm_simple_kms_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/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index 0b5f3accb1e4..a6eecf6f9065 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -268,7 +268,7 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc, return 0; vblank_cleanup: - drm_crtc_vblank_put(&amdgpu_crtc->base); + drm_crtc_vblank_put(crtc); pflip_cleanup: if (unlikely(amdgpu_bo_reserve(new_rbo, false) != 0)) { diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index 112e358f0f9b..c1b04e9aab57 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2719,13 +2719,13 @@ static void dce_v10_0_crtc_dpms(struct drm_crtc *crtc, int mode) type = amdgpu_crtc_idx_to_irq_type(adev, amdgpu_crtc->crtc_id); amdgpu_irq_update(adev, &adev->crtc_irq, type); amdgpu_irq_update(adev, &adev->pageflip_irq, type); - drm_vblank_on(dev, amdgpu_crtc->crtc_id); + drm_crtc_vblank_on(crtc); dce_v10_0_crtc_load_lut(crtc); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - drm_vblank_off(dev, amdgpu_crtc->crtc_id); + drm_crtc_vblank_off(crtc); if (amdgpu_crtc->enabled) { dce_v10_0_vga_enable(crtc, true); amdgpu_atombios_crtc_blank(crtc, ATOM_ENABLE); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index b522fa2435a8..c90408bc0fde 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -2730,13 +2730,13 @@ static void dce_v11_0_crtc_dpms(struct drm_crtc *crtc, int mode) type = amdgpu_crtc_idx_to_irq_type(adev, amdgpu_crtc->crtc_id); amdgpu_irq_update(adev, &adev->crtc_irq, type); amdgpu_irq_update(adev, &adev->pageflip_irq, type); - drm_vblank_on(dev, amdgpu_crtc->crtc_id); + drm_crtc_vblank_on(crtc); dce_v11_0_crtc_load_lut(crtc); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - drm_vblank_off(dev, amdgpu_crtc->crtc_id); + drm_crtc_vblank_off(crtc); if (amdgpu_crtc->enabled) { dce_v11_0_vga_enable(crtc, true); amdgpu_atombios_crtc_blank(crtc, ATOM_ENABLE); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index b50ed72feedb..300ff4aab0fd 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -2626,13 +2626,13 @@ static void dce_v8_0_crtc_dpms(struct drm_crtc *crtc, int mode) type = amdgpu_crtc_idx_to_irq_type(adev, amdgpu_crtc->crtc_id); amdgpu_irq_update(adev, &adev->crtc_irq, type); amdgpu_irq_update(adev, &adev->pageflip_irq, type); - drm_vblank_on(dev, amdgpu_crtc->crtc_id); + drm_crtc_vblank_on(crtc); dce_v8_0_crtc_load_lut(crtc); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - drm_vblank_off(dev, amdgpu_crtc->crtc_id); + drm_crtc_vblank_off(crtc); if (amdgpu_crtc->enabled) { dce_v8_0_vga_enable(crtc, true); amdgpu_atombios_crtc_blank(crtc, ATOM_ENABLE); diff --git a/drivers/gpu/drm/arc/arcpgu.h b/drivers/gpu/drm/arc/arcpgu.h index 329ac7570911..e8fcf3ab1d9a 100644 --- a/drivers/gpu/drm/arc/arcpgu.h +++ b/drivers/gpu/drm/arc/arcpgu.h @@ -22,7 +22,6 @@ struct arcpgu_drm_private { struct clk *clk; struct drm_fbdev_cma *fbdev; struct drm_framebuffer *fb; - struct list_head event_list; struct drm_crtc crtc; struct drm_plane *plane; }; diff --git a/drivers/gpu/drm/arc/arcpgu_crtc.c b/drivers/gpu/drm/arc/arcpgu_crtc.c index 92f8beff8e60..ee0a61c2861b 100644 --- a/drivers/gpu/drm/arc/arcpgu_crtc.c +++ b/drivers/gpu/drm/arc/arcpgu_crtc.c @@ -145,20 +145,14 @@ static int arc_pgu_crtc_atomic_check(struct drm_crtc *crtc, static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *state) { - struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); - unsigned long flags; - - if (crtc->state->event) { - struct drm_pending_vblank_event *event = crtc->state->event; + struct drm_pending_vblank_event *event = crtc->state->event; + if (event) { crtc->state->event = NULL; - event->pipe = drm_crtc_index(crtc); - - WARN_ON(drm_crtc_vblank_get(crtc) != 0); - spin_lock_irqsave(&crtc->dev->event_lock, flags); - list_add_tail(&event->base.link, &arcpgu->event_list); - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); } } diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index 07c1bdeca489..381c5fcbf903 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -32,17 +32,11 @@ static void arcpgu_fb_output_poll_changed(struct drm_device *dev) drm_fbdev_cma_hotplug_event(arcpgu->fbdev); } -static int arcpgu_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, bool async) -{ - return drm_atomic_helper_commit(dev, state, false); -} - static struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = arcpgu_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check, - .atomic_commit = arcpgu_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, }; static void arcpgu_setup_mode_config(struct drm_device *drm) @@ -81,22 +75,6 @@ static const struct file_operations arcpgu_drm_ops = { .mmap = arcpgu_gem_mmap, }; -static void arcpgu_preclose(struct drm_device *drm, struct drm_file *file) -{ - struct arcpgu_drm_private *arcpgu = drm->dev_private; - struct drm_pending_vblank_event *e, *t; - unsigned long flags; - - spin_lock_irqsave(&drm->event_lock, flags); - list_for_each_entry_safe(e, t, &arcpgu->event_list, base.link) { - if (e->base.file_priv != file) - continue; - list_del(&e->base.link); - kfree(&e->base); - } - spin_unlock_irqrestore(&drm->event_lock, flags); -} - static void arcpgu_lastclose(struct drm_device *drm) { struct arcpgu_drm_private *arcpgu = drm->dev_private; @@ -122,8 +100,6 @@ static int arcpgu_load(struct drm_device *drm) if (IS_ERR(arcpgu->clk)) return PTR_ERR(arcpgu->clk); - INIT_LIST_HEAD(&arcpgu->event_list); - arcpgu_setup_mode_config(drm); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -193,7 +169,6 @@ int arcpgu_unload(struct drm_device *drm) static struct drm_driver arcpgu_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .preclose = arcpgu_preclose, .lastclose = arcpgu_lastclose, .name = "drm-arcpgu", .desc = "ARC PGU Controller", diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c index 08b6baeb320d..b7a8b2ac4055 100644 --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c @@ -46,23 +46,6 @@ static int arcpgu_drm_connector_get_modes(struct drm_connector *connector) return sfuncs->get_modes(&slave->base, connector); } -struct drm_encoder * -arcpgu_drm_connector_best_encoder(struct drm_connector *connector) -{ - struct drm_encoder_slave *slave; - struct arcpgu_drm_connector *con = - container_of(connector, struct arcpgu_drm_connector, connector); - - slave = con->encoder_slave; - if (slave == NULL) { - dev_err(connector->dev->dev, - "connector_best_encoder: cannot find slave encoder for connector\n"); - return NULL; - } - - return &slave->base; -} - static enum drm_connector_status arcpgu_drm_connector_detect(struct drm_connector *connector, bool force) { @@ -97,7 +80,6 @@ static void arcpgu_drm_connector_destroy(struct drm_connector *connector) static const struct drm_connector_helper_funcs arcpgu_drm_connector_helper_funcs = { .get_modes = arcpgu_drm_connector_get_modes, - .best_encoder = arcpgu_drm_connector_best_encoder, }; static const struct drm_connector_funcs arcpgu_drm_connector_funcs = { diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 49e586d67595..74279be20b75 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -106,17 +106,11 @@ static void hdlcd_fb_output_poll_changed(struct drm_device *drm) drm_fbdev_cma_hotplug_event(hdlcd->fbdev); } -static int hdlcd_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, bool nonblock) -{ - return drm_atomic_helper_commit(dev, state, false); -} - static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = hdlcd_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check, - .atomic_commit = hdlcd_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, }; static void hdlcd_setup_mode_config(struct drm_device *drm) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 6485fa5bee8b..9ecf16c7911d 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -519,7 +519,7 @@ static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev, } /* Swap the state, this is the point of no return. */ - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true); if (async) queue_work(dc->wq, &commit->work); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 39802c0539b6..473a475f27b1 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -113,21 +113,9 @@ static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector, return atmel_hlcdc_dc_mode_valid(rgb->dc, mode); } - - -static struct drm_encoder * -atmel_hlcdc_rgb_best_encoder(struct drm_connector *connector) -{ - struct atmel_hlcdc_rgb_output *rgb = - drm_connector_to_atmel_hlcdc_rgb_output(connector); - - return &rgb->encoder; -} - static const struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helper_funcs = { .get_modes = atmel_hlcdc_panel_get_modes, .mode_valid = atmel_hlcdc_rgb_mode_valid, - .best_encoder = atmel_hlcdc_rgb_best_encoder, }; static enum drm_connector_status diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c index d087b054c360..f9f03bcba0af 100644 --- a/drivers/gpu/drm/bridge/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c @@ -986,16 +986,8 @@ unlock: return num_modes; } -static struct drm_encoder *anx78xx_best_encoder(struct drm_connector *connector) -{ - struct anx78xx *anx78xx = connector_to_anx78xx(connector); - - return anx78xx->bridge.encoder; -} - static const struct drm_connector_helper_funcs anx78xx_connector_helper_funcs = { .get_modes = anx78xx_get_modes, - .best_encoder = anx78xx_best_encoder, }; static enum drm_connector_status anx78xx_detect(struct drm_connector *connector, diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index c9d941283d30..70b1f7d4270b 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -1476,15 +1476,6 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector, return mode_status; } -static struct drm_encoder *dw_hdmi_connector_best_encoder(struct drm_connector - *connector) -{ - struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, - connector); - - return hdmi->encoder; -} - static void dw_hdmi_connector_destroy(struct drm_connector *connector) { drm_connector_unregister(connector); @@ -1525,7 +1516,7 @@ static const struct drm_connector_funcs dw_hdmi_atomic_connector_funcs = { static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { .get_modes = dw_hdmi_connector_get_modes, .mode_valid = dw_hdmi_connector_mode_valid, - .best_encoder = dw_hdmi_connector_best_encoder, + .best_encoder = drm_atomic_helper_best_encoder, }; static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index 7ecd59f70b8e..93f3dacf9e27 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -235,16 +235,8 @@ out: return num_modes; } -static struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) -{ - struct ptn3460_bridge *ptn_bridge = connector_to_ptn3460(connector); - - return ptn_bridge->bridge.encoder; -} - static const struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = { .get_modes = ptn3460_get_modes, - .best_encoder = ptn3460_best_encoder, }; static enum drm_connector_status ptn3460_detect(struct drm_connector *connector, diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index be881e9fef8f..5cd8dd7e5904 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -474,18 +474,8 @@ static int ps8622_get_modes(struct drm_connector *connector) return drm_panel_get_modes(ps8622->panel); } -static struct drm_encoder *ps8622_best_encoder(struct drm_connector *connector) -{ - struct ps8622_bridge *ps8622; - - ps8622 = connector_to_ps8622(connector); - - return ps8622->bridge.encoder; -} - static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = { .get_modes = ps8622_get_modes, - .best_encoder = ps8622_best_encoder, }; static enum drm_connector_status ps8622_detect(struct drm_connector *connector, diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 5e4b820a977c..d99ab2f6663f 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -33,6 +33,20 @@ #include "drm_crtc_internal.h" +static void crtc_commit_free(struct kref *kref) +{ + struct drm_crtc_commit *commit = + container_of(kref, struct drm_crtc_commit, ref); + + kfree(commit); +} + +void drm_crtc_commit_put(struct drm_crtc_commit *commit) +{ + kref_put(&commit->ref, crtc_commit_free); +} +EXPORT_SYMBOL(drm_crtc_commit_put); + /** * drm_atomic_state_default_release - * release memory initialized by drm_atomic_state_init @@ -148,6 +162,14 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) crtc->funcs->atomic_destroy_state(crtc, state->crtcs[i].state); + + if (state->crtcs[i].commit) { + kfree(state->crtcs[i].commit->event); + state->crtcs[i].commit->event = NULL; + drm_crtc_commit_put(state->crtcs[i].commit); + } + + state->crtcs[i].commit = NULL; state->crtcs[i].ptr = NULL; state->crtcs[i].state = NULL; } diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index bb98d74d1a2e..de7fddce3cef 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -465,7 +465,7 @@ mode_fixup(struct drm_atomic_state *state) * times for the same update, e.g. when the ->atomic_check functions depend upon * the adjusted dotclock for fifo space allocation and watermark computation. * - * RETURNS + * RETURNS: * Zero for success or -errno */ int @@ -579,7 +579,7 @@ EXPORT_SYMBOL(drm_atomic_helper_check_modeset); * It also sets crtc_state->planes_changed to indicate that a crtc has * updated planes. * - * RETURNS + * RETURNS: * Zero for success or -errno */ int @@ -647,7 +647,7 @@ EXPORT_SYMBOL(drm_atomic_helper_check_planes); * ->atomic_check functions depend upon an updated adjusted_mode.clock to * e.g. properly compute watermarks. * - * RETURNS + * RETURNS: * Zero for success or -errno */ int drm_atomic_helper_check(struct drm_device *dev, @@ -1120,22 +1120,17 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); /** - * drm_atomic_helper_commit - commit validated state object - * @dev: DRM device - * @state: the driver state object - * @nonblocking: whether nonblocking behavior is requested. + * drm_atomic_helper_commit_tail - commit atomic update to hardware + * @state: new modeset state to be committed * - * This function commits a with drm_atomic_helper_check() pre-validated state - * object. This can still fail when e.g. the framebuffer reservation fails. For - * now this doesn't implement nonblocking commits. + * This is the default implemenation for the ->atomic_commit_tail() hook of the + * &drm_mode_config_helper_funcs vtable. * - * Note that right now this function does not support nonblocking commits, hence - * driver writers must implement their own version for now. Also note that the - * default ordering of how the various stages are called is to match the legacy - * modeset helper library closest. One peculiarity of that is that it doesn't - * mesh well with runtime PM at all. + * Note that the default ordering of how the various stages are called is to + * match the legacy modeset helper library closest. One peculiarity of that is + * that it doesn't mesh well with runtime PM at all. * - * For drivers supporting runtime PM the recommended sequence is + * For drivers supporting runtime PM the recommended sequence is instead :: * * drm_atomic_helper_commit_modeset_disables(dev, state); * @@ -1143,9 +1138,75 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); * * drm_atomic_helper_commit_planes(dev, state, true); * - * See the kerneldoc entries for these three functions for more details. + * for committing the atomic update to hardware. See the kerneldoc entries for + * these three functions for more details. + */ +void drm_atomic_helper_commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + + drm_atomic_helper_commit_modeset_disables(dev, state); + + drm_atomic_helper_commit_planes(dev, state, false); + + drm_atomic_helper_commit_modeset_enables(dev, state); + + drm_atomic_helper_commit_hw_done(state); + + drm_atomic_helper_wait_for_vblanks(dev, state); + + drm_atomic_helper_cleanup_planes(dev, state); +} +EXPORT_SYMBOL(drm_atomic_helper_commit_tail); + +static void commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + struct drm_mode_config_helper_funcs *funcs; + + funcs = dev->mode_config.helper_private; + + drm_atomic_helper_wait_for_fences(dev, state); + + drm_atomic_helper_wait_for_dependencies(state); + + if (funcs && funcs->atomic_commit_tail) + funcs->atomic_commit_tail(state); + else + drm_atomic_helper_commit_tail(state); + + drm_atomic_helper_commit_cleanup_done(state); + + drm_atomic_state_free(state); +} + +static void commit_work(struct work_struct *work) +{ + struct drm_atomic_state *state = container_of(work, + struct drm_atomic_state, + commit_work); + commit_tail(state); +} + +/** + * drm_atomic_helper_commit - commit validated state object + * @dev: DRM device + * @state: the driver state object + * @nonblock: whether nonblocking behavior is requested. + * + * This function commits a with drm_atomic_helper_check() pre-validated state + * object. This can still fail when e.g. the framebuffer reservation fails. This + * function implements nonblocking commits, using + * drm_atomic_helper_setup_commit() and related functions. + * + * Note that right now this function does not support nonblocking commits, hence + * driver writers must implement their own version for now. + * + * Committing the actual hardware state is done through the + * ->atomic_commit_tail() callback of the &drm_mode_config_helper_funcs vtable, + * or it's default implementation drm_atomic_helper_commit_tail(). * - * RETURNS + * RETURNS: * Zero for success or -errno. */ int drm_atomic_helper_commit(struct drm_device *dev, @@ -1154,8 +1215,11 @@ int drm_atomic_helper_commit(struct drm_device *dev, { int ret; - if (nonblock) - return -EBUSY; + ret = drm_atomic_helper_setup_commit(state, nonblock); + if (ret) + return ret; + + INIT_WORK(&state->commit_work, commit_work); ret = drm_atomic_helper_prepare_planes(dev, state); if (ret) @@ -1167,7 +1231,7 @@ int drm_atomic_helper_commit(struct drm_device *dev, * the software side now. */ - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true); /* * Everything below can be run asynchronously without the need to grab @@ -1183,21 +1247,16 @@ int drm_atomic_helper_commit(struct drm_device *dev, * update. Which is important since compositors need to figure out the * composition of the next frame right after having submitted the * current layout. + * + * NOTE: Commit work has multiple phases, first hardware commit, then + * cleanup. We want them to overlap, hence need system_unbound_wq to + * make sure work items don't artifically stall on each another. */ - drm_atomic_helper_wait_for_fences(dev, state); - - drm_atomic_helper_commit_modeset_disables(dev, state); - - drm_atomic_helper_commit_planes(dev, state, false); - - drm_atomic_helper_commit_modeset_enables(dev, state); - - drm_atomic_helper_wait_for_vblanks(dev, state); - - drm_atomic_helper_cleanup_planes(dev, state); - - drm_atomic_state_free(state); + if (nonblock) + queue_work(system_unbound_wq, &state->commit_work); + else + commit_tail(state); return 0; } @@ -1206,12 +1265,7 @@ EXPORT_SYMBOL(drm_atomic_helper_commit); /** * DOC: implementing nonblocking commit * - * For now the atomic helpers don't support nonblocking commit directly. If - * there is real need it could be added though, using the dma-buf fence - * infrastructure for generic synchronization with outstanding rendering. - * - * For now drivers have to implement nonblocking commit themselves, with the - * following sequence being the recommended one: + * Nonblocking atomic commits have to be implemented in the following sequence: * * 1. Run drm_atomic_helper_prepare_planes() first. This is the only function * which commit needs to call which can fail, so we want to run it first and @@ -1223,10 +1277,14 @@ EXPORT_SYMBOL(drm_atomic_helper_commit); * cancelled updates. Note that it is important to ensure that the framebuffer * cleanup is still done when cancelling. * - * For sufficient parallelism it is recommended to have a work item per crtc - * (for updates which don't touch global state) and a global one. Then we only - * need to synchronize with the crtc work items for changed crtcs and the global - * work item, which allows nice concurrent updates on disjoint sets of crtcs. + * Asynchronous workers need to have sufficient parallelism to be able to run + * different atomic commits on different CRTCs in parallel. The simplest way to + * achive this is by running them on the &system_unbound_wq work queue. Note + * that drivers are not required to split up atomic commits and run an + * individual commit in parallel - userspace is supposed to do that if it cares. + * But it might be beneficial to do that for modesets, since those necessarily + * must be done as one global operation, and enabling or disabling a CRTC can + * take a long time. But even that is not required. * * 3. The software state is updated synchronously with * drm_atomic_helper_swap_state(). Doing this under the protection of all modeset @@ -1239,8 +1297,310 @@ EXPORT_SYMBOL(drm_atomic_helper_commit); * commit helpers: a) pre-plane commit b) plane commit c) post-plane commit and * then cleaning up the framebuffers after the old framebuffer is no longer * being displayed. + * + * The above scheme is implemented in the atomic helper libraries in + * drm_atomic_helper_commit() using a bunch of helper functions. See + * drm_atomic_helper_setup_commit() for a starting point. */ +static int stall_checks(struct drm_crtc *crtc, bool nonblock) +{ + struct drm_crtc_commit *commit, *stall_commit = NULL; + bool completed = true; + int i; + long ret = 0; + + spin_lock(&crtc->commit_lock); + i = 0; + list_for_each_entry(commit, &crtc->commit_list, commit_entry) { + if (i == 0) { + completed = try_wait_for_completion(&commit->flip_done); + /* Userspace is not allowed to get ahead of the previous + * commit with nonblocking ones. */ + if (!completed && nonblock) { + spin_unlock(&crtc->commit_lock); + return -EBUSY; + } + } else if (i == 1) { + stall_commit = commit; + drm_crtc_commit_get(stall_commit); + break; + } + + i++; + } + spin_unlock(&crtc->commit_lock); + + if (!stall_commit) + return 0; + + /* We don't want to let commits get ahead of cleanup work too much, + * stalling on 2nd previous commit means triple-buffer won't ever stall. + */ + ret = wait_for_completion_interruptible_timeout(&stall_commit->cleanup_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] cleanup_done timed out\n", + crtc->base.id, crtc->name); + + drm_crtc_commit_put(stall_commit); + + return ret < 0 ? ret : 0; +} + +/** + * drm_atomic_helper_setup_commit - setup possibly nonblocking commit + * @state: new modeset state to be committed + * @nonblock: whether nonblocking behavior is requested. + * + * This function prepares @state to be used by the atomic helper's support for + * nonblocking commits. Drivers using the nonblocking commit infrastructure + * should always call this function from their ->atomic_commit hook. + * + * To be able to use this support drivers need to use a few more helper + * functions. drm_atomic_helper_wait_for_dependencies() must be called before + * actually committing the hardware state, and for nonblocking commits this call + * must be placed in the async worker. See also drm_atomic_helper_swap_state() + * and it's stall parameter, for when a driver's commit hooks look at the + * ->state pointers of struct &drm_crtc, &drm_plane or &drm_connector directly. + * + * Completion of the hardware commit step must be signalled using + * drm_atomic_helper_commit_hw_done(). After this step the driver is not allowed + * to read or change any permanent software or hardware modeset state. The only + * exception is state protected by other means than &drm_modeset_lock locks. + * Only the free standing @state with pointers to the old state structures can + * be inspected, e.g. to clean up old buffers using + * drm_atomic_helper_cleanup_planes(). + * + * At the very end, before cleaning up @state drivers must call + * drm_atomic_helper_commit_cleanup_done(). + * + * This is all implemented by in drm_atomic_helper_commit(), giving drivers a + * complete and esay-to-use default implementation of the atomic_commit() hook. + * + * The tracking of asynchronously executed and still pending commits is done + * using the core structure &drm_crtc_commit. + * + * By default there's no need to clean up resources allocated by this function + * explicitly: drm_atomic_state_default_clear() will take care of that + * automatically. + * + * Returns: + * + * 0 on success. -EBUSY when userspace schedules nonblocking commits too fast, + * -ENOMEM on allocation failures and -EINTR when a signal is pending. + */ +int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, + bool nonblock) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i, ret; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + commit = kzalloc(sizeof(*commit), GFP_KERNEL); + if (!commit) + return -ENOMEM; + + init_completion(&commit->flip_done); + init_completion(&commit->hw_done); + init_completion(&commit->cleanup_done); + INIT_LIST_HEAD(&commit->commit_entry); + kref_init(&commit->ref); + commit->crtc = crtc; + + state->crtcs[i].commit = commit; + + ret = stall_checks(crtc, nonblock); + if (ret) + return ret; + + /* Drivers only send out events when at least either current or + * new CRTC state is active. Complete right away if everything + * stays off. */ + if (!crtc->state->active && !crtc_state->active) { + complete_all(&commit->flip_done); + continue; + } + + /* Legacy cursor updates are fully unsynced. */ + if (state->legacy_cursor_update) { + complete_all(&commit->flip_done); + continue; + } + + if (!crtc_state->event) { + commit->event = kzalloc(sizeof(*commit->event), + GFP_KERNEL); + if (!commit->event) + return -ENOMEM; + + crtc_state->event = commit->event; + } + + crtc_state->event->base.completion = &commit->flip_done; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_helper_setup_commit); + + +static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc) +{ + struct drm_crtc_commit *commit; + int i = 0; + + list_for_each_entry(commit, &crtc->commit_list, commit_entry) { + /* skip the first entry, that's the current commit */ + if (i == 1) + return commit; + i++; + } + + return NULL; +} + +/** + * drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits + * @state: new modeset state to be committed + * + * This function waits for all preceeding commits that touch the same CRTC as + * @state to both be committed to the hardware (as signalled by + * drm_atomic_helper_commit_hw_done) and executed by the hardware (as signalled + * by calling drm_crtc_vblank_send_event on the event member of + * &drm_crtc_state). + * + * This is part of the atomic helper support for nonblocking commits, see + * drm_atomic_helper_setup_commit() for an overview. + */ +void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i; + long ret; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + spin_lock(&crtc->commit_lock); + commit = preceeding_commit(crtc); + if (commit) + drm_crtc_commit_get(commit); + spin_unlock(&crtc->commit_lock); + + if (!commit) + continue; + + ret = wait_for_completion_timeout(&commit->hw_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n", + crtc->base.id, crtc->name); + + /* Currently no support for overwriting flips, hence + * stall for previous one to execute completely. */ + ret = wait_for_completion_timeout(&commit->flip_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", + crtc->base.id, crtc->name); + + drm_crtc_commit_put(commit); + } +} +EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); + +/** + * drm_atomic_helper_commit_hw_done - setup possible nonblocking commit + * @state: new modeset state to be committed + * + * This function is used to signal completion of the hardware commit step. After + * this step the driver is not allowed to read or change any permanent software + * or hardware modeset state. The only exception is state protected by other + * means than &drm_modeset_lock locks. + * + * Drivers should try to postpone any expensive or delayed cleanup work after + * this function is called. + * + * This is part of the atomic helper support for nonblocking commits, see + * drm_atomic_helper_setup_commit() for an overview. + */ +void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + commit = state->crtcs[i].commit; + if (!commit) + continue; + + /* backend must have consumed any event by now */ + WARN_ON(crtc->state->event); + spin_lock(&crtc->commit_lock); + complete_all(&commit->hw_done); + spin_unlock(&crtc->commit_lock); + } +} +EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done); + +/** + * drm_atomic_helper_commit_cleanup_done - signal completion of commit + * @state: new modeset state to be committed + * + * This signals completion of the atomic update @state, including any cleanup + * work. If used, it must be called right before calling + * drm_atomic_state_free(). + * + * This is part of the atomic helper support for nonblocking commits, see + * drm_atomic_helper_setup_commit() for an overview. + */ +void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i; + long ret; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + commit = state->crtcs[i].commit; + if (WARN_ON(!commit)) + continue; + + spin_lock(&crtc->commit_lock); + complete_all(&commit->cleanup_done); + WARN_ON(!try_wait_for_completion(&commit->hw_done)); + + /* commit_list borrows our reference, need to remove before we + * clean up our drm_atomic_state. But only after it actually + * completed, otherwise subsequent commits won't stall properly. */ + if (try_wait_for_completion(&commit->flip_done)) + goto del_commit; + + spin_unlock(&crtc->commit_lock); + + /* We must wait for the vblank event to signal our completion + * before releasing our reference, since the vblank work does + * not hold a reference of its own. */ + ret = wait_for_completion_timeout(&commit->flip_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", + crtc->base.id, crtc->name); + + spin_lock(&crtc->commit_lock); +del_commit: + list_del(&commit->commit_entry); + spin_unlock(&crtc->commit_lock); + } +} +EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done); + /** * drm_atomic_helper_prepare_planes - prepare plane resources before commit * @dev: DRM device @@ -1538,8 +1898,8 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes); /** * drm_atomic_helper_swap_state - store atomic state into current sw state - * @dev: DRM device * @state: atomic state + * @stall: stall for proceeding commits * * This function stores the atomic state into the current state pointers in all * driver objects. It should be called after all failing steps have been done @@ -1560,17 +1920,45 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes); * * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3 * contains the old state. Also do any other cleanup required with that state. + * + * @stall must be set when nonblocking commits for this driver directly access + * the ->state pointer of &drm_plane, &drm_crtc or &drm_connector. With the + * current atomic helpers this is almost always the case, since the helpers + * don't pass the right state structures to the callbacks. */ -void drm_atomic_helper_swap_state(struct drm_device *dev, - struct drm_atomic_state *state) +void drm_atomic_helper_swap_state(struct drm_atomic_state *state, + bool stall) { int i; + long ret; struct drm_connector *connector; struct drm_connector_state *conn_state; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; struct drm_plane *plane; struct drm_plane_state *plane_state; + struct drm_crtc_commit *commit; + + if (stall) { + for_each_crtc_in_state(state, crtc, crtc_state, i) { + spin_lock(&crtc->commit_lock); + commit = list_first_entry_or_null(&crtc->commit_list, + struct drm_crtc_commit, commit_entry); + if (commit) + drm_crtc_commit_get(commit); + spin_unlock(&crtc->commit_lock); + + if (!commit) + continue; + + ret = wait_for_completion_timeout(&commit->hw_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n", + crtc->base.id, crtc->name); + drm_crtc_commit_put(commit); + } + } for_each_connector_in_state(state, connector, conn_state, i) { connector->state->state = state; @@ -1582,6 +1970,15 @@ void drm_atomic_helper_swap_state(struct drm_device *dev, crtc->state->state = state; swap(state->crtcs[i].state, crtc->state); crtc->state->state = NULL; + + if (state->crtcs[i].commit) { + spin_lock(&crtc->commit_lock); + list_add(&state->crtcs[i].commit->commit_entry, + &crtc->commit_list); + spin_unlock(&crtc->commit_lock); + + state->crtcs[i].commit->event = NULL; + } } for_each_plane_in_state(state, plane, plane_state, i) { diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index df91dfe506eb..4ec35f9e6de5 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -239,37 +239,6 @@ const char *drm_get_subpixel_order_name(enum subpixel_order order) } EXPORT_SYMBOL(drm_get_subpixel_order_name); -static char printable_char(int c) -{ - return isascii(c) && isprint(c) ? c : '?'; -} - -/** - * drm_get_format_name - return a string for drm fourcc format - * @format: format to compute name of - * - * Note that the buffer used by this function is globally shared and owned by - * the function itself. - * - * FIXME: This isn't really multithreading safe. - */ -const char *drm_get_format_name(uint32_t format) -{ - static char buf[32]; - - snprintf(buf, sizeof(buf), - "%c%c%c%c %s-endian (0x%08x)", - printable_char(format & 0xff), - printable_char((format >> 8) & 0xff), - printable_char((format >> 16) & 0xff), - printable_char((format >> 24) & 0x7f), - format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little", - format); - - return buf; -} -EXPORT_SYMBOL(drm_get_format_name); - /* * Internal function to assign a slot in the object idr and optionally * register the object into the idr. @@ -669,6 +638,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, crtc->dev = dev; crtc->funcs = funcs; + INIT_LIST_HEAD(&crtc->commit_list); + spin_lock_init(&crtc->commit_lock); + drm_modeset_lock_init(&crtc->mutex); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) @@ -5503,264 +5475,6 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, } /** - * drm_fb_get_bpp_depth - get the bpp/depth values for format - * @format: pixel format (DRM_FORMAT_*) - * @depth: storage for the depth value - * @bpp: storage for the bpp value - * - * This only supports RGB formats here for compat with code that doesn't use - * pixel formats directly yet. - */ -void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, - int *bpp) -{ - switch (format) { - case DRM_FORMAT_C8: - case DRM_FORMAT_RGB332: - case DRM_FORMAT_BGR233: - *depth = 8; - *bpp = 8; - break; - case DRM_FORMAT_XRGB1555: - case DRM_FORMAT_XBGR1555: - case DRM_FORMAT_RGBX5551: - case DRM_FORMAT_BGRX5551: - case DRM_FORMAT_ARGB1555: - case DRM_FORMAT_ABGR1555: - case DRM_FORMAT_RGBA5551: - case DRM_FORMAT_BGRA5551: - *depth = 15; - *bpp = 16; - break; - case DRM_FORMAT_RGB565: - case DRM_FORMAT_BGR565: - *depth = 16; - *bpp = 16; - break; - case DRM_FORMAT_RGB888: - case DRM_FORMAT_BGR888: - *depth = 24; - *bpp = 24; - break; - case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_XBGR8888: - case DRM_FORMAT_RGBX8888: - case DRM_FORMAT_BGRX8888: - *depth = 24; - *bpp = 32; - break; - case DRM_FORMAT_XRGB2101010: - case DRM_FORMAT_XBGR2101010: - case DRM_FORMAT_RGBX1010102: - case DRM_FORMAT_BGRX1010102: - case DRM_FORMAT_ARGB2101010: - case DRM_FORMAT_ABGR2101010: - case DRM_FORMAT_RGBA1010102: - case DRM_FORMAT_BGRA1010102: - *depth = 30; - *bpp = 32; - break; - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_ABGR8888: - case DRM_FORMAT_RGBA8888: - case DRM_FORMAT_BGRA8888: - *depth = 32; - *bpp = 32; - break; - default: - DRM_DEBUG_KMS("unsupported pixel format %s\n", - drm_get_format_name(format)); - *depth = 0; - *bpp = 0; - break; - } -} -EXPORT_SYMBOL(drm_fb_get_bpp_depth); - -/** - * drm_format_num_planes - get the number of planes for format - * @format: pixel format (DRM_FORMAT_*) - * - * Returns: - * The number of planes used by the specified pixel format. - */ -int drm_format_num_planes(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YVU410: - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YVU411: - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YVU422: - case DRM_FORMAT_YUV444: - case DRM_FORMAT_YVU444: - return 3; - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - case DRM_FORMAT_NV16: - case DRM_FORMAT_NV61: - case DRM_FORMAT_NV24: - case DRM_FORMAT_NV42: - return 2; - default: - return 1; - } -} -EXPORT_SYMBOL(drm_format_num_planes); - -/** - * drm_format_plane_cpp - determine the bytes per pixel value - * @format: pixel format (DRM_FORMAT_*) - * @plane: plane index - * - * Returns: - * The bytes per pixel value for the specified plane. - */ -int drm_format_plane_cpp(uint32_t format, int plane) -{ - unsigned int depth; - int bpp; - - if (plane >= drm_format_num_planes(format)) - return 0; - - switch (format) { - case DRM_FORMAT_YUYV: - case DRM_FORMAT_YVYU: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - return 2; - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - case DRM_FORMAT_NV16: - case DRM_FORMAT_NV61: - case DRM_FORMAT_NV24: - case DRM_FORMAT_NV42: - return plane ? 2 : 1; - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YVU410: - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YVU411: - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YVU422: - case DRM_FORMAT_YUV444: - case DRM_FORMAT_YVU444: - return 1; - default: - drm_fb_get_bpp_depth(format, &depth, &bpp); - return bpp >> 3; - } -} -EXPORT_SYMBOL(drm_format_plane_cpp); - -/** - * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor - * @format: pixel format (DRM_FORMAT_*) - * - * Returns: - * The horizontal chroma subsampling factor for the - * specified pixel format. - */ -int drm_format_horz_chroma_subsampling(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YVU411: - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YVU410: - return 4; - case DRM_FORMAT_YUYV: - case DRM_FORMAT_YVYU: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - case DRM_FORMAT_NV16: - case DRM_FORMAT_NV61: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YVU422: - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - return 2; - default: - return 1; - } -} -EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); - -/** - * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor - * @format: pixel format (DRM_FORMAT_*) - * - * Returns: - * The vertical chroma subsampling factor for the - * specified pixel format. - */ -int drm_format_vert_chroma_subsampling(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YVU410: - return 4; - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - return 2; - default: - return 1; - } -} -EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); - -/** - * drm_format_plane_width - width of the plane given the first plane - * @width: width of the first plane - * @format: pixel format - * @plane: plane index - * - * Returns: - * The width of @plane, given that the width of the first plane is @width. - */ -int drm_format_plane_width(int width, uint32_t format, int plane) -{ - if (plane >= drm_format_num_planes(format)) - return 0; - - if (plane == 0) - return width; - - return width / drm_format_horz_chroma_subsampling(format); -} -EXPORT_SYMBOL(drm_format_plane_width); - -/** - * drm_format_plane_height - height of the plane given the first plane - * @height: height of the first plane - * @format: pixel format - * @plane: plane index - * - * Returns: - * The height of @plane, given that the height of the first plane is @height. - */ -int drm_format_plane_height(int height, uint32_t format, int plane) -{ - if (plane >= drm_format_num_planes(format)) - return 0; - - if (plane == 0) - return height; - - return height / drm_format_vert_chroma_subsampling(format); -} -EXPORT_SYMBOL(drm_format_plane_height); - -/** * drm_rotation_simplify() - Try to simplify the rotation * @rotation: Rotation to be simplified * @supported_rotations: Supported rotations diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index bff89226a344..8b2582aeaab6 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -605,8 +605,6 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL); if (ret) goto err_minors; - - WARN_ON(driver->suspend || driver->resume); } if (drm_core_check_feature(dev, DRIVER_RENDER)) { diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 2e7ef0b325e2..c0b0c718994a 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -346,6 +346,7 @@ static int drm_fbdev_cma_defio_init(struct fb_info *fbi, fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); if (!fbdefio || !fbops) { kfree(fbdefio); + kfree(fbops); return -ENOMEM; } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index ba5aac7276e4..0bac5246e5a7 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -385,7 +385,7 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) drm_warn_on_modeset_not_all_locked(dev); - if (fb_helper->atomic) + if (dev->mode_config.funcs->atomic_commit) return restore_fbdev_mode_atomic(fb_helper); drm_for_each_plane(plane, dev) { @@ -716,8 +716,6 @@ int drm_fb_helper_init(struct drm_device *dev, i++; } - fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC); - return 0; out_free: drm_fb_helper_crtc_free(fb_helper); @@ -1344,7 +1342,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, return -EBUSY; } - if (fb_helper->atomic) { + if (dev->mode_config.funcs->atomic_commit) { ret = pan_display_atomic(var, info); goto unlock; } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 64121f567234..a27bc7cda975 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -797,6 +797,12 @@ void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) { assert_spin_locked(&dev->event_lock); + if (e->completion) { + /* ->completion might disappear as soon as it signalled. */ + complete_all(e->completion); + e->completion = NULL; + } + if (e->fence) { fence_signal(e->fence); fence_put(e->fence); diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c new file mode 100644 index 000000000000..0645c85d5f95 --- /dev/null +++ b/drivers/gpu/drm/drm_fourcc.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * DRM core format related functions + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <linux/bug.h> +#include <linux/ctype.h> +#include <linux/export.h> +#include <linux/kernel.h> + +#include <drm/drmP.h> +#include <drm/drm_fourcc.h> + +static char printable_char(int c) +{ + return isascii(c) && isprint(c) ? c : '?'; +} + +/** + * drm_get_format_name - return a string for drm fourcc format + * @format: format to compute name of + * + * Note that the buffer used by this function is globally shared and owned by + * the function itself. + * + * FIXME: This isn't really multithreading safe. + */ +const char *drm_get_format_name(uint32_t format) +{ + static char buf[32]; + + snprintf(buf, sizeof(buf), + "%c%c%c%c %s-endian (0x%08x)", + printable_char(format & 0xff), + printable_char((format >> 8) & 0xff), + printable_char((format >> 16) & 0xff), + printable_char((format >> 24) & 0x7f), + format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little", + format); + + return buf; +} +EXPORT_SYMBOL(drm_get_format_name); + +/** + * drm_fb_get_bpp_depth - get the bpp/depth values for format + * @format: pixel format (DRM_FORMAT_*) + * @depth: storage for the depth value + * @bpp: storage for the bpp value + * + * This only supports RGB formats here for compat with code that doesn't use + * pixel formats directly yet. + */ +void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, + int *bpp) +{ + switch (format) { + case DRM_FORMAT_C8: + case DRM_FORMAT_RGB332: + case DRM_FORMAT_BGR233: + *depth = 8; + *bpp = 8; + break; + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_BGRA5551: + *depth = 15; + *bpp = 16; + break; + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + *depth = 16; + *bpp = 16; + break; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_BGR888: + *depth = 24; + *bpp = 24; + break; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + *depth = 24; + *bpp = 32; + break; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_RGBX1010102: + case DRM_FORMAT_BGRX1010102: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + *depth = 30; + *bpp = 32; + break; + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + *depth = 32; + *bpp = 32; + break; + default: + DRM_DEBUG_KMS("unsupported pixel format %s\n", + drm_get_format_name(format)); + *depth = 0; + *bpp = 0; + break; + } +} +EXPORT_SYMBOL(drm_fb_get_bpp_depth); + +/** + * drm_format_num_planes - get the number of planes for format + * @format: pixel format (DRM_FORMAT_*) + * + * Returns: + * The number of planes used by the specified pixel format. + */ +int drm_format_num_planes(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_YUV410: + case DRM_FORMAT_YVU410: + case DRM_FORMAT_YUV411: + case DRM_FORMAT_YVU411: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YUV444: + case DRM_FORMAT_YVU444: + return 3; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV42: + return 2; + default: + return 1; + } +} +EXPORT_SYMBOL(drm_format_num_planes); + +/** + * drm_format_plane_cpp - determine the bytes per pixel value + * @format: pixel format (DRM_FORMAT_*) + * @plane: plane index + * + * Returns: + * The bytes per pixel value for the specified plane. + */ +int drm_format_plane_cpp(uint32_t format, int plane) +{ + unsigned int depth; + int bpp; + + if (plane >= drm_format_num_planes(format)) + return 0; + + switch (format) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + return 2; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV42: + return plane ? 2 : 1; + case DRM_FORMAT_YUV410: + case DRM_FORMAT_YVU410: + case DRM_FORMAT_YUV411: + case DRM_FORMAT_YVU411: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YUV444: + case DRM_FORMAT_YVU444: + return 1; + default: + drm_fb_get_bpp_depth(format, &depth, &bpp); + return bpp >> 3; + } +} +EXPORT_SYMBOL(drm_format_plane_cpp); + +/** + * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor + * @format: pixel format (DRM_FORMAT_*) + * + * Returns: + * The horizontal chroma subsampling factor for the + * specified pixel format. + */ +int drm_format_horz_chroma_subsampling(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_YUV411: + case DRM_FORMAT_YVU411: + case DRM_FORMAT_YUV410: + case DRM_FORMAT_YVU410: + return 4; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + return 2; + default: + return 1; + } +} +EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); + +/** + * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor + * @format: pixel format (DRM_FORMAT_*) + * + * Returns: + * The vertical chroma subsampling factor for the + * specified pixel format. + */ +int drm_format_vert_chroma_subsampling(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_YUV410: + case DRM_FORMAT_YVU410: + return 4; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + return 2; + default: + return 1; + } +} +EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); + +/** + * drm_format_plane_width - width of the plane given the first plane + * @width: width of the first plane + * @format: pixel format + * @plane: plane index + * + * Returns: + * The width of @plane, given that the width of the first plane is @width. + */ +int drm_format_plane_width(int width, uint32_t format, int plane) +{ + if (plane >= drm_format_num_planes(format)) + return 0; + + if (plane == 0) + return width; + + return width / drm_format_horz_chroma_subsampling(format); +} +EXPORT_SYMBOL(drm_format_plane_width); + +/** + * drm_format_plane_height - height of the plane given the first plane + * @height: height of the first plane + * @format: pixel format + * @plane: plane index + * + * Returns: + * The height of @plane, given that the height of the first plane is @height. + */ +int drm_format_plane_height(int height, uint32_t format, int plane) +{ + if (plane >= drm_format_num_planes(format)) + return 0; + + if (plane == 0) + return height; + + return height / drm_format_vert_chroma_subsampling(format); +} +EXPORT_SYMBOL(drm_format_plane_height); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 4dc41ff388f9..76e39c50c90c 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1001,34 +1001,6 @@ static void send_vblank_event(struct drm_device *dev, } /** - * drm_arm_vblank_event - arm vblank event after pageflip - * @dev: DRM device - * @pipe: CRTC index - * @e: the event to prepare to send - * - * A lot of drivers need to generate vblank events for the very next vblank - * interrupt. For example when the page flip interrupt happens when the page - * flip gets armed, but not when it actually executes within the next vblank - * period. This helper function implements exactly the required vblank arming - * behaviour. - * - * Caller must hold event lock. Caller must also hold a vblank reference for - * the event @e, which will be dropped when the next vblank arrives. - * - * This is the legacy version of drm_crtc_arm_vblank_event(). - */ -void drm_arm_vblank_event(struct drm_device *dev, unsigned int pipe, - struct drm_pending_vblank_event *e) -{ - assert_spin_locked(&dev->event_lock); - - e->pipe = pipe; - e->event.sequence = drm_vblank_count(dev, pipe); - list_add_tail(&e->base.link, &dev->vblank_event_list); -} -EXPORT_SYMBOL(drm_arm_vblank_event); - -/** * drm_crtc_arm_vblank_event - arm vblank event after pageflip * @crtc: the source CRTC of the vblank event * @e: the event to send @@ -1041,32 +1013,35 @@ EXPORT_SYMBOL(drm_arm_vblank_event); * * Caller must hold event lock. Caller must also hold a vblank reference for * the event @e, which will be dropped when the next vblank arrives. - * - * This is the native KMS version of drm_arm_vblank_event(). */ void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, struct drm_pending_vblank_event *e) { - drm_arm_vblank_event(crtc->dev, drm_crtc_index(crtc), e); + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + + assert_spin_locked(&dev->event_lock); + + e->pipe = pipe; + e->event.sequence = drm_vblank_count(dev, pipe); + list_add_tail(&e->base.link, &dev->vblank_event_list); } EXPORT_SYMBOL(drm_crtc_arm_vblank_event); /** - * drm_send_vblank_event - helper to send vblank event after pageflip - * @dev: DRM device - * @pipe: CRTC index + * drm_crtc_send_vblank_event - helper to send vblank event after pageflip + * @crtc: the source CRTC of the vblank event * @e: the event to send * * Updates sequence # and timestamp on event, and sends it to userspace. * Caller must hold event lock. - * - * This is the legacy version of drm_crtc_send_vblank_event(). */ -void drm_send_vblank_event(struct drm_device *dev, unsigned int pipe, - struct drm_pending_vblank_event *e) +void drm_crtc_send_vblank_event(struct drm_crtc *crtc, + struct drm_pending_vblank_event *e) { + struct drm_device *dev = crtc->dev; + unsigned int seq, pipe = drm_crtc_index(crtc); struct timeval now; - unsigned int seq; if (dev->num_crtcs > 0) { seq = drm_vblank_count_and_time(dev, pipe, &now); @@ -1078,23 +1053,6 @@ void drm_send_vblank_event(struct drm_device *dev, unsigned int pipe, e->pipe = pipe; send_vblank_event(dev, e, seq, &now); } -EXPORT_SYMBOL(drm_send_vblank_event); - -/** - * drm_crtc_send_vblank_event - helper to send vblank event after pageflip - * @crtc: the source CRTC of the vblank event - * @e: the event to send - * - * Updates sequence # and timestamp on event, and sends it to userspace. - * Caller must hold event lock. - * - * This is the native KMS version of drm_send_vblank_event(). - */ -void drm_crtc_send_vblank_event(struct drm_crtc *crtc, - struct drm_pending_vblank_event *e) -{ - drm_send_vblank_event(crtc->dev, drm_crtc_index(crtc), e); -} EXPORT_SYMBOL(drm_crtc_send_vblank_event); /** @@ -1150,7 +1108,7 @@ static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe) * Returns: * Zero on success or a negative error code on failure. */ -int drm_vblank_get(struct drm_device *dev, unsigned int pipe) +static int drm_vblank_get(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; unsigned long irqflags; @@ -1176,7 +1134,6 @@ int drm_vblank_get(struct drm_device *dev, unsigned int pipe) return ret; } -EXPORT_SYMBOL(drm_vblank_get); /** * drm_crtc_vblank_get - get a reference count on vblank events @@ -1185,8 +1142,6 @@ EXPORT_SYMBOL(drm_vblank_get); * Acquire a reference count on vblank events to avoid having them disabled * while in use. * - * This is the native kms version of drm_vblank_get(). - * * Returns: * Zero on success or a negative error code on failure. */ @@ -1206,7 +1161,7 @@ EXPORT_SYMBOL(drm_crtc_vblank_get); * * This is the legacy version of drm_crtc_vblank_put(). */ -void drm_vblank_put(struct drm_device *dev, unsigned int pipe) +static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; @@ -1227,7 +1182,6 @@ void drm_vblank_put(struct drm_device *dev, unsigned int pipe) jiffies + ((drm_vblank_offdelay * HZ)/1000)); } } -EXPORT_SYMBOL(drm_vblank_put); /** * drm_crtc_vblank_put - give up ownership of vblank events @@ -1235,8 +1189,6 @@ EXPORT_SYMBOL(drm_vblank_put); * * Release ownership of a given vblank counter, turning off interrupts * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. - * - * This is the native kms version of drm_vblank_put(). */ void drm_crtc_vblank_put(struct drm_crtc *crtc) { diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 7938ce7ebed8..49311fc61d5d 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -60,6 +60,21 @@ static int mipi_dsi_device_match(struct device *dev, struct device_driver *drv) return 0; } +static int mipi_dsi_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); + int err; + + err = of_device_uevent_modalias(dev, env); + if (err != -ENODEV) + return err; + + add_uevent_var(env, "MODALIAS=%s%s", MIPI_DSI_MODULE_PREFIX, + dsi->name); + + return 0; +} + static const struct dev_pm_ops mipi_dsi_device_pm_ops = { .runtime_suspend = pm_generic_runtime_suspend, .runtime_resume = pm_generic_runtime_resume, @@ -74,6 +89,7 @@ static const struct dev_pm_ops mipi_dsi_device_pm_ops = { static struct bus_type mipi_dsi_bus_type = { .name = "mipi-dsi", .match = mipi_dsi_device_match, + .uevent = mipi_dsi_uevent, .pm = &mipi_dsi_device_pm_ops, }; diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index aab0f3f1f42d..780589b420a4 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -593,7 +593,7 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev, get_dma_buf(dma_buf); } - /* drm_gem_handle_create_tail unlocks dev->object_name_lock. */ + /* _handle_create_tail unconditionally unlocks dev->object_name_lock. */ ret = drm_gem_handle_create_tail(file_priv, obj, handle); drm_gem_object_unreference_unlocked(obj); if (ret) @@ -601,11 +601,10 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev, ret = drm_prime_add_buf_handle(&file_priv->prime, dma_buf, *handle); + mutex_unlock(&file_priv->prime.lock); if (ret) goto fail; - mutex_unlock(&file_priv->prime.lock); - dma_buf_put(dma_buf); return 0; @@ -615,11 +614,14 @@ fail: * to detach.. which seems ok.. */ drm_gem_handle_delete(file_priv, *handle); + dma_buf_put(dma_buf); + return ret; + out_unlock: mutex_unlock(&dev->object_name_lock); out_put: - dma_buf_put(dma_buf); mutex_unlock(&file_priv->prime.lock); + dma_buf_put(dma_buf); return ret; } EXPORT_SYMBOL(drm_gem_prime_fd_to_handle); diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c new file mode 100644 index 000000000000..b2071d495ada --- /dev/null +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_simple_kms_helper.h> +#include <linux/slab.h> + +/** + * DOC: overview + * + * This helper library provides helpers for drivers for simple display + * hardware. + * + * drm_simple_display_pipe_init() initializes a simple display pipeline + * which has only one full-screen scanout buffer feeding one output. The + * pipeline is represented by struct &drm_simple_display_pipe and binds + * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed + * entity. Some flexibility for code reuse is provided through a separately + * allocated &drm_connector object and supporting optional &drm_bridge + * encoder drivers. + */ + +static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc) +{ + struct drm_simple_display_pipe *pipe; + + pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); + if (!pipe->funcs || !pipe->funcs->enable) + return; + + pipe->funcs->enable(pipe, crtc->state); +} + +static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc) +{ + struct drm_simple_display_pipe *pipe; + + pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); + if (!pipe->funcs || !pipe->funcs->disable) + return; + + pipe->funcs->disable(pipe); +} + +static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = { + .disable = drm_simple_kms_crtc_disable, + .enable = drm_simple_kms_crtc_enable, +}; + +static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *plane_state) +{ + struct drm_rect src = { + .x1 = plane_state->src_x, + .y1 = plane_state->src_y, + .x2 = plane_state->src_x + plane_state->src_w, + .y2 = plane_state->src_y + plane_state->src_h, + }; + struct drm_rect dest = { + .x1 = plane_state->crtc_x, + .y1 = plane_state->crtc_y, + .x2 = plane_state->crtc_x + plane_state->crtc_w, + .y2 = plane_state->crtc_y + plane_state->crtc_h, + }; + struct drm_rect clip = { 0 }; + struct drm_simple_display_pipe *pipe; + struct drm_crtc_state *crtc_state; + bool visible; + int ret; + + pipe = container_of(plane, struct drm_simple_display_pipe, plane); + crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state, + &pipe->crtc); + if (crtc_state->enable != !!plane_state->crtc) + return -EINVAL; /* plane must match crtc enable state */ + + if (!crtc_state->enable) + return 0; /* nothing to check when disabling or disabled */ + + clip.x2 = crtc_state->adjusted_mode.hdisplay; + clip.y2 = crtc_state->adjusted_mode.vdisplay; + ret = drm_plane_helper_check_update(plane, &pipe->crtc, + plane_state->fb, + &src, &dest, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true, &visible); + if (ret) + return ret; + + if (!visible) + return -EINVAL; + + if (!pipe->funcs || !pipe->funcs->check) + return 0; + + return pipe->funcs->check(pipe, plane_state, crtc_state); +} + +static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *pstate) +{ + struct drm_simple_display_pipe *pipe; + + pipe = container_of(plane, struct drm_simple_display_pipe, plane); + if (!pipe->funcs || !pipe->funcs->update) + return; + + pipe->funcs->update(pipe, pstate); +} + +static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = { + .atomic_check = drm_simple_kms_plane_atomic_check, + .atomic_update = drm_simple_kms_plane_atomic_update, +}; + +static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +/** + * drm_simple_display_pipe_init - Initialize a simple display pipeline + * @dev: DRM device + * @pipe: simple display pipe object to initialize + * @funcs: callbacks for the display pipe (optional) + * @formats: array of supported formats (%DRM_FORMAT_*) + * @format_count: number of elements in @formats + * @connector: connector to attach and register + * + * Sets up a display pipeline which consist of a really simple + * plane-crtc-encoder pipe coupled with the provided connector. + * Teardown of a simple display pipe is all handled automatically by the drm + * core through calling drm_mode_config_cleanup(). Drivers afterwards need to + * release the memory for the structure themselves. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int drm_simple_display_pipe_init(struct drm_device *dev, + struct drm_simple_display_pipe *pipe, + const struct drm_simple_display_pipe_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + struct drm_connector *connector) +{ + struct drm_encoder *encoder = &pipe->encoder; + struct drm_plane *plane = &pipe->plane; + struct drm_crtc *crtc = &pipe->crtc; + int ret; + + pipe->connector = connector; + pipe->funcs = funcs; + + drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs); + ret = drm_universal_plane_init(dev, plane, 0, + &drm_simple_kms_plane_funcs, + formats, format_count, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ret; + + drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs); + ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, + &drm_simple_kms_crtc_funcs, NULL); + if (ret) + return ret; + + encoder->possible_crtcs = 1 << drm_crtc_index(crtc); + ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); + if (ret) + return ret; + + return drm_mode_connector_attach_encoder(connector, encoder); +} +EXPORT_SYMBOL(drm_simple_display_pipe_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index fa7fadce8063..32dd821b7202 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -32,75 +32,6 @@ static struct device_type drm_sysfs_device_minor = { struct class *drm_class; -/** - * __drm_class_suspend - internal DRM class suspend routine - * @dev: Linux device to suspend - * @state: power state to enter - * - * Just figures out what the actual struct drm_device associated with - * @dev is and calls its suspend hook, if present. - */ -static int __drm_class_suspend(struct device *dev, pm_message_t state) -{ - if (dev->type == &drm_sysfs_device_minor) { - struct drm_minor *drm_minor = to_drm_minor(dev); - struct drm_device *drm_dev = drm_minor->dev; - - if (drm_minor->type == DRM_MINOR_LEGACY && - !drm_core_check_feature(drm_dev, DRIVER_MODESET) && - drm_dev->driver->suspend) - return drm_dev->driver->suspend(drm_dev, state); - } - return 0; -} - -/** - * drm_class_suspend - internal DRM class suspend hook. Simply calls - * __drm_class_suspend() with the correct pm state. - * @dev: Linux device to suspend - */ -static int drm_class_suspend(struct device *dev) -{ - return __drm_class_suspend(dev, PMSG_SUSPEND); -} - -/** - * drm_class_freeze - internal DRM class freeze hook. Simply calls - * __drm_class_suspend() with the correct pm state. - * @dev: Linux device to freeze - */ -static int drm_class_freeze(struct device *dev) -{ - return __drm_class_suspend(dev, PMSG_FREEZE); -} - -/** - * drm_class_resume - DRM class resume hook - * @dev: Linux device to resume - * - * Just figures out what the actual struct drm_device associated with - * @dev is and calls its resume hook, if present. - */ -static int drm_class_resume(struct device *dev) -{ - if (dev->type == &drm_sysfs_device_minor) { - struct drm_minor *drm_minor = to_drm_minor(dev); - struct drm_device *drm_dev = drm_minor->dev; - - if (drm_minor->type == DRM_MINOR_LEGACY && - !drm_core_check_feature(drm_dev, DRIVER_MODESET) && - drm_dev->driver->resume) - return drm_dev->driver->resume(drm_dev); - } - return 0; -} - -static const struct dev_pm_ops drm_class_dev_pm_ops = { - .suspend = drm_class_suspend, - .resume = drm_class_resume, - .freeze = drm_class_freeze, -}; - static char *drm_devnode(struct device *dev, umode_t *mode) { return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); @@ -131,8 +62,6 @@ int drm_sysfs_init(void) if (IS_ERR(drm_class)) return PTR_ERR(drm_class); - drm_class->pm = &drm_class_dev_pm_ops; - err = class_create_file(drm_class, &class_attr_version.attr); if (err) { class_destroy(drm_class); diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 5e38e749ac17..ad6b73c7fc59 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -93,17 +93,8 @@ static int exynos_dpi_get_modes(struct drm_connector *connector) return 0; } -static struct drm_encoder * -exynos_dpi_best_encoder(struct drm_connector *connector) -{ - struct exynos_dpi *ctx = connector_to_dpi(connector); - - return &ctx->encoder; -} - static const struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = { .get_modes = exynos_dpi_get_modes, - .best_encoder = exynos_dpi_best_encoder, }; static int exynos_dpi_create_connector(struct drm_encoder *encoder) diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 843b21c540b3..4a679fb9bb02 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -299,7 +299,7 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, priv->pending |= commit->crtcs; spin_unlock(&priv->lock); - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true); if (nonblock) schedule_work(&commit->work); diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 601ecf8006a7..e07cb1fe4860 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1566,17 +1566,8 @@ static int exynos_dsi_get_modes(struct drm_connector *connector) return 0; } -static struct drm_encoder * -exynos_dsi_best_encoder(struct drm_connector *connector) -{ - struct exynos_dsi *dsi = connector_to_dsi(connector); - - return &dsi->encoder; -} - static const struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = { .get_modes = exynos_dsi_get_modes, - .best_encoder = exynos_dsi_best_encoder, }; static int exynos_dsi_create_connector(struct drm_encoder *encoder) diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 55f1d37c666a..77f12c00abf9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -242,7 +242,7 @@ exynos_drm_plane_check_size(const struct exynos_drm_plane_config *config, state->v_ratio == (1 << 15)) height_ok = true; - if (width_ok & height_ok) + if (width_ok && height_ok) return 0; DRM_DEBUG_KMS("scaling mode is not supported"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 608b0afa337f..e8f6c92b2a36 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -378,16 +378,8 @@ static int vidi_get_modes(struct drm_connector *connector) return drm_add_edid_modes(connector, edid); } -static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector) -{ - struct vidi_context *ctx = ctx_from_connector(connector); - - return &ctx->encoder; -} - static const struct drm_connector_helper_funcs vidi_connector_helper_funcs = { .get_modes = vidi_get_modes, - .best_encoder = vidi_best_encoder, }; static int vidi_create_connector(struct drm_encoder *encoder) diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 58de5a430508..1625d7c8a319 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -937,17 +937,9 @@ static int hdmi_mode_valid(struct drm_connector *connector, return MODE_OK; } -static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector) -{ - struct hdmi_context *hdata = connector_to_hdmi(connector); - - return &hdata->encoder; -} - static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { .get_modes = hdmi_get_modes, .mode_valid = hdmi_mode_valid, - .best_encoder = hdmi_best_encoder, }; static int hdmi_create_connector(struct drm_encoder *encoder) diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c index 89c0084c2814..706de3278f1c 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c @@ -22,20 +22,21 @@ #include "fsl_dcu_drm_drv.h" #include "fsl_dcu_drm_plane.h" -static void fsl_dcu_drm_crtc_atomic_begin(struct drm_crtc *crtc, +static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { -} + struct drm_pending_vblank_event *event = crtc->state->event; -static int fsl_dcu_drm_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - return 0; -} + if (event) { + crtc->state->event = NULL; -static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) -{ + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } } static void fsl_dcu_drm_disable_crtc(struct drm_crtc *crtc) @@ -117,8 +118,6 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) } static const struct drm_crtc_helper_funcs fsl_dcu_drm_crtc_helper_funcs = { - .atomic_begin = fsl_dcu_drm_crtc_atomic_begin, - .atomic_check = fsl_dcu_drm_crtc_atomic_check, .atomic_flush = fsl_dcu_drm_crtc_atomic_flush, .disable = fsl_dcu_drm_disable_crtc, .enable = fsl_dcu_drm_crtc_enable, diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index 98c998da91eb..0b0989e503ea 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -102,14 +102,6 @@ static const struct drm_connector_funcs fsl_dcu_drm_connector_funcs = { .reset = drm_atomic_helper_connector_reset, }; -static struct drm_encoder * -fsl_dcu_drm_connector_best_encoder(struct drm_connector *connector) -{ - struct fsl_dcu_drm_connector *fsl_con = to_fsl_dcu_connector(connector); - - return fsl_con->encoder; -} - static int fsl_dcu_drm_connector_get_modes(struct drm_connector *connector) { struct fsl_dcu_drm_connector *fsl_connector; @@ -136,7 +128,6 @@ static int fsl_dcu_drm_connector_mode_valid(struct drm_connector *connector, } static const struct drm_connector_helper_funcs connector_helper_funcs = { - .best_encoder = fsl_dcu_drm_connector_best_encoder, .get_modes = fsl_dcu_drm_connector_get_modes, .mode_valid = fsl_dcu_drm_connector_mode_valid, }; diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index 5b636bf0b467..1a1cf7a3b5ef 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -282,7 +282,7 @@ void gma_crtc_dpms(struct drm_crtc *crtc, int mode) REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); /* Turn off vblank interrupts */ - drm_vblank_off(dev, pipe); + drm_crtc_vblank_off(crtc); /* Wait for vblank for the disable to take effect */ gma_wait_for_vblank(dev); diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index fba6372d060e..ed76baad525f 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -502,13 +502,6 @@ static void ade_crtc_disable(struct drm_crtc *crtc) acrtc->enable = false; } -static int ade_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - /* do nothing */ - return 0; -} - static void ade_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct ade_crtc *acrtc = to_ade_crtc(crtc); @@ -537,6 +530,7 @@ static void ade_crtc_atomic_flush(struct drm_crtc *crtc, { struct ade_crtc *acrtc = to_ade_crtc(crtc); struct ade_hw_ctx *ctx = acrtc->ctx; + struct drm_pending_vblank_event *event = crtc->state->event; void __iomem *base = ctx->base; /* only crtc is enabled regs take effect */ @@ -545,12 +539,22 @@ static void ade_crtc_atomic_flush(struct drm_crtc *crtc, /* flush ade registers */ writel(ADE_ENABLE, base + ADE_EN); } + + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } } static const struct drm_crtc_helper_funcs ade_crtc_helper_funcs = { .enable = ade_crtc_enable, .disable = ade_crtc_disable, - .atomic_check = ade_crtc_atomic_check, .mode_set_nofb = ade_crtc_mode_set_nofb, .atomic_begin = ade_crtc_atomic_begin, .atomic_flush = ade_crtc_atomic_flush, diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e4f2c55d9697..614ac085e51f 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2393,16 +2393,16 @@ static int i915_ppgtt_info(struct seq_file *m, void *data) task = get_pid_task(file->pid, PIDTYPE_PID); if (!task) { ret = -ESRCH; - goto out_put; + goto out_unlock; } seq_printf(m, "\nproc: %s\n", task->comm); put_task_struct(task); idr_for_each(&file_priv->context_idr, per_file_ctx, (void *)(unsigned long)m); } +out_unlock: mutex_unlock(&dev->filelist_mutex); -out_put: intel_runtime_pm_put(dev_priv); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 622968161ac7..9465de4135aa 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -753,7 +753,6 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = { static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = { .mode_valid = intel_crt_mode_valid, .get_modes = intel_crt_get_modes, - .best_encoder = intel_best_encoder, }; static const struct drm_encoder_funcs intel_crt_enc_funcs = { diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 60cba1956c0d..49322f6cfa2b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13726,7 +13726,7 @@ static int intel_atomic_commit(struct drm_device *dev, return ret; } - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true); dev_priv->wm.distrust_bios_wm = false; dev_priv->wm.skl_results = intel_state->wm_results; intel_shared_dpll_commit(state); @@ -16267,14 +16267,6 @@ void intel_modeset_cleanup(struct drm_device *dev) intel_teardown_gmbus(dev); } -/* - * Return which encoder is currently attached for connector. - */ -struct drm_encoder *intel_best_encoder(struct drm_connector *connector) -{ - return &intel_attached_encoder(connector)->base; -} - void intel_connector_attach_encoder(struct intel_connector *connector, struct intel_encoder *encoder) { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f97cd5305e4c..be083519dac9 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4580,7 +4580,6 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = { static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = { .get_modes = intel_dp_get_modes, .mode_valid = intel_dp_mode_valid, - .best_encoder = intel_best_encoder, }; static const struct drm_encoder_funcs intel_dp_enc_funcs = { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index ebe7b3427e2e..270da8de0acf 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1154,7 +1154,6 @@ struct intel_connector *intel_connector_alloc(void); bool intel_connector_get_hw_state(struct intel_connector *connector); void intel_connector_attach_encoder(struct intel_connector *connector, struct intel_encoder *encoder); -struct drm_encoder *intel_best_encoder(struct drm_connector *connector); struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, struct drm_crtc *crtc); enum pipe intel_get_pipe_from_connector(struct intel_connector *connector); diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index c70132aa91d5..a2ead5eac336 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -1379,7 +1379,6 @@ static const struct drm_encoder_funcs intel_dsi_funcs = { static const struct drm_connector_helper_funcs intel_dsi_connector_helper_funcs = { .get_modes = intel_dsi_get_modes, .mode_valid = intel_dsi_mode_valid, - .best_encoder = intel_best_encoder, }; static const struct drm_connector_funcs intel_dsi_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index a456f2eb68b6..c86f88ed92fd 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -351,7 +351,6 @@ static const struct drm_connector_funcs intel_dvo_connector_funcs = { static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = { .mode_valid = intel_dvo_mode_valid, .get_modes = intel_dvo_get_modes, - .best_encoder = intel_best_encoder, }; static void intel_dvo_enc_destroy(struct drm_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index ef8e67690f3d..4c725ad6fb54 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -724,8 +724,6 @@ int intel_fbdev_init(struct drm_device *dev) return ret; } - ifbdev->helper.atomic = true; - dev_priv->fbdev = ifbdev; INIT_WORK(&dev_priv->fbdev_suspend_work, intel_fbdev_suspend_worker); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index eb455ea6ea92..6b29da9bba38 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1782,7 +1782,6 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = { static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = { .get_modes = intel_hdmi_get_modes, .mode_valid = intel_hdmi_mode_valid, - .best_encoder = intel_best_encoder, }; static const struct drm_encoder_funcs intel_hdmi_enc_funcs = { diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 62eaa895fe5b..e06b9036bebc 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -547,7 +547,6 @@ static int intel_lvds_set_property(struct drm_connector *connector, static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { .get_modes = intel_lvds_get_modes, .mode_valid = intel_lvds_mode_valid, - .best_encoder = intel_best_encoder, }; static const struct drm_connector_funcs intel_lvds_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 1a71456bd12a..ab2d0658abe6 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2191,7 +2191,6 @@ static const struct drm_connector_funcs intel_sdvo_connector_funcs = { static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = { .get_modes = intel_sdvo_get_modes, .mode_valid = intel_sdvo_mode_valid, - .best_encoder = intel_best_encoder, }; static void intel_sdvo_enc_destroy(struct drm_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 1f3a0e1e1a1f..7ac9e9b0e2c3 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1512,7 +1512,6 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = { .mode_valid = intel_tv_mode_valid, .get_modes = intel_tv_get_modes, - .best_encoder = intel_best_encoder, }; static const struct drm_encoder_funcs intel_tv_enc_funcs = { diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 06a417b2f91e..c33bf98c5d5e 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -91,7 +91,7 @@ static int mtk_atomic_commit(struct drm_device *drm, mutex_lock(&private->commit.lock); flush_work(&private->commit.work); - drm_atomic_helper_swap_state(drm, state); + drm_atomic_helper_swap_state(state, true); if (async) mtk_atomic_schedule(private, state); diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 769559124562..28b2044ed9f2 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -575,14 +575,6 @@ static int mtk_dsi_connector_get_modes(struct drm_connector *connector) return drm_panel_get_modes(dsi->panel); } -static struct drm_encoder *mtk_dsi_connector_best_encoder( - struct drm_connector *connector) -{ - struct mtk_dsi *dsi = connector_to_dsi(connector); - - return &dsi->encoder; -} - static const struct drm_encoder_helper_funcs mtk_dsi_encoder_helper_funcs = { .mode_fixup = mtk_dsi_encoder_mode_fixup, .mode_set = mtk_dsi_encoder_mode_set, @@ -603,7 +595,6 @@ static const struct drm_connector_funcs mtk_dsi_connector_funcs = { static const struct drm_connector_helper_funcs mtk_dsi_connector_helper_funcs = { .get_modes = mtk_dsi_connector_get_modes, - .best_encoder = mtk_dsi_connector_best_encoder, }; static int mtk_drm_attach_bridge(struct drm_bridge *bridge, diff --git a/drivers/gpu/drm/msm/edp/edp_connector.c b/drivers/gpu/drm/msm/edp/edp_connector.c index 72360cd038c0..5960628ceb93 100644 --- a/drivers/gpu/drm/msm/edp/edp_connector.c +++ b/drivers/gpu/drm/msm/edp/edp_connector.c @@ -91,15 +91,6 @@ static int edp_connector_mode_valid(struct drm_connector *connector, return MODE_OK; } -static struct drm_encoder * -edp_connector_best_encoder(struct drm_connector *connector) -{ - struct edp_connector *edp_connector = to_edp_connector(connector); - - DBG(""); - return edp_connector->edp->encoder; -} - static const struct drm_connector_funcs edp_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = edp_connector_detect, @@ -113,7 +104,6 @@ static const struct drm_connector_funcs edp_connector_funcs = { static const struct drm_connector_helper_funcs edp_connector_helper_funcs = { .get_modes = edp_connector_get_modes, .mode_valid = edp_connector_mode_valid, - .best_encoder = edp_connector_best_encoder, }; /* initialize connector */ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c index b15d72683112..a2515b466ce5 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c @@ -406,13 +406,6 @@ static int msm_hdmi_connector_mode_valid(struct drm_connector *connector, return 0; } -static struct drm_encoder * -msm_hdmi_connector_best_encoder(struct drm_connector *connector) -{ - struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); - return hdmi_connector->hdmi->encoder; -} - static const struct drm_connector_funcs hdmi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = hdmi_connector_detect, @@ -426,7 +419,6 @@ static const struct drm_connector_funcs hdmi_connector_funcs = { static const struct drm_connector_helper_funcs msm_hdmi_connector_helper_funcs = { .get_modes = msm_hdmi_connector_get_modes, .mode_valid = msm_hdmi_connector_mode_valid, - .best_encoder = msm_hdmi_connector_best_encoder, }; /* initialize connector */ diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c index 2648cd7631ef..353429b05733 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c @@ -90,14 +90,6 @@ static int mdp4_lvds_connector_mode_valid(struct drm_connector *connector, return MODE_OK; } -static struct drm_encoder * -mdp4_lvds_connector_best_encoder(struct drm_connector *connector) -{ - struct mdp4_lvds_connector *mdp4_lvds_connector = - to_mdp4_lvds_connector(connector); - return mdp4_lvds_connector->encoder; -} - static const struct drm_connector_funcs mdp4_lvds_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = mdp4_lvds_connector_detect, @@ -111,7 +103,6 @@ static const struct drm_connector_funcs mdp4_lvds_connector_funcs = { static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs = { .get_modes = mdp4_lvds_connector_get_modes, .mode_valid = mdp4_lvds_connector_mode_valid, - .best_encoder = mdp4_lvds_connector_best_encoder, }; /* initialize connector */ diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 8c3b463620bd..4a8a6f1f1151 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -238,7 +238,7 @@ int msm_atomic_commit(struct drm_device *dev, * the software side now. */ - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true); /* * Everything below can be run asynchronously without the need to grab diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 7c77f960c8b8..6072fe292db8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -760,12 +760,11 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Initialize a page flip struct */ *s = (struct nouveau_page_flip_state) - { { }, event, nouveau_crtc(crtc)->index, - fb->bits_per_pixel, fb->pitches[0], crtc->x, crtc->y, + { { }, event, crtc, fb->bits_per_pixel, fb->pitches[0], new_bo->bo.offset }; /* Keep vblanks on during flip, for the target crtc of this flip */ - drm_vblank_get(dev, nouveau_crtc(crtc)->index); + drm_crtc_vblank_get(crtc); /* Emit a page flip */ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { @@ -810,7 +809,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, return 0; fail_unreserve: - drm_vblank_put(dev, nouveau_crtc(crtc)->index); + drm_crtc_vblank_put(crtc); ttm_bo_unreserve(&old_bo->bo); fail_unpin: mutex_unlock(&cli->mutex); @@ -842,17 +841,17 @@ nouveau_finish_page_flip(struct nouveau_channel *chan, s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head); if (s->event) { if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { - drm_arm_vblank_event(dev, s->crtc, s->event); + drm_crtc_arm_vblank_event(s->crtc, s->event); } else { - drm_send_vblank_event(dev, s->crtc, s->event); + drm_crtc_send_vblank_event(s->crtc, s->event); /* Give up ownership of vblank for page-flipped crtc */ - drm_vblank_put(dev, s->crtc); + drm_crtc_vblank_put(s->crtc); } } else { /* Give up ownership of vblank for page-flipped crtc */ - drm_vblank_put(dev, s->crtc); + drm_crtc_vblank_put(s->crtc); } list_del(&s->head); @@ -873,9 +872,10 @@ nouveau_flip_complete(struct nvif_notify *notify) if (!nouveau_finish_page_flip(chan, &state)) { if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { - nv_set_crtc_base(drm->dev, state.crtc, state.offset + - state.y * state.pitch + - state.x * state.bpp / 8); + nv_set_crtc_base(drm->dev, drm_crtc_index(state.crtc), + state.offset + state.crtc->y * + state.pitch + state.crtc->x * + state.bpp / 8); } } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 24273bacd885..0420ee861ea4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -28,7 +28,8 @@ int nouveau_framebuffer_init(struct drm_device *, struct nouveau_framebuffer *, struct nouveau_page_flip_state { struct list_head head; struct drm_pending_vblank_event *event; - int crtc, bpp, pitch, x, y; + struct drm_crtc *crtc; + int bpp, pitch; u64 offset; }; diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index ce2d67b6a8c7..137fe690a0da 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -32,7 +32,6 @@ struct omap_connector { struct drm_connector base; struct omap_dss_device *dssdev; - struct drm_encoder *encoder; bool hdmi_mode; }; @@ -256,13 +255,6 @@ static int omap_connector_mode_valid(struct drm_connector *connector, return ret; } -struct drm_encoder *omap_connector_attached_encoder( - struct drm_connector *connector) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - return omap_connector->encoder; -} - static const struct drm_connector_funcs omap_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .reset = drm_atomic_helper_connector_reset, @@ -276,7 +268,6 @@ static const struct drm_connector_funcs omap_connector_funcs = { static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { .get_modes = omap_connector_get_modes, .mode_valid = omap_connector_mode_valid, - .best_encoder = omap_connector_attached_encoder, }; /* initialize connector */ @@ -296,7 +287,6 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, goto fail; omap_connector->dssdev = dssdev; - omap_connector->encoder = encoder; connector = &omap_connector->base; diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 3b702230a88c..6b97011154bf 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -174,7 +174,7 @@ static int omap_atomic_commit(struct drm_device *dev, spin_unlock(&priv->commit.lock); /* Swap the state, this is the point of no return. */ - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true); if (nonblock) schedule_work(&commit->work); diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 2e216e2ea78c..e91763d5d800 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -276,14 +276,14 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) atombios_enable_crtc_memreq(crtc, ATOM_ENABLE); atombios_blank_crtc(crtc, ATOM_DISABLE); if (dev->num_crtcs > radeon_crtc->crtc_id) - drm_vblank_on(dev, radeon_crtc->crtc_id); + drm_crtc_vblank_on(crtc); radeon_crtc_load_lut(crtc); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: if (dev->num_crtcs > radeon_crtc->crtc_id) - drm_vblank_off(dev, radeon_crtc->crtc_id); + drm_crtc_vblank_off(crtc); if (radeon_crtc->enabled) atombios_blank_crtc(crtc, ATOM_ENABLE); if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev)) diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index e85c7a2f565b..3965d1916b9c 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -627,7 +627,7 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, return 0; vblank_cleanup: - drm_crtc_vblank_put(&radeon_crtc->base); + drm_crtc_vblank_put(crtc); pflip_cleanup: if (unlikely(radeon_bo_reserve(new_rbo, false) != 0)) { diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index 478d4099b0d0..d0de4022fff9 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c @@ -332,14 +332,14 @@ static void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) WREG32_P(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl, ~(mask | crtc_ext_cntl)); } if (dev->num_crtcs > radeon_crtc->crtc_id) - drm_vblank_on(dev, radeon_crtc->crtc_id); + drm_crtc_vblank_on(crtc); radeon_crtc_load_lut(crtc); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: if (dev->num_crtcs > radeon_crtc->crtc_id) - drm_vblank_off(dev, radeon_crtc->crtc_id); + drm_crtc_vblank_off(crtc); if (radeon_crtc->crtc_id) WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~(RADEON_CRTC2_EN | mask)); else { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 4e939e41f030..55149e9ce28e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -27,18 +27,6 @@ #include "rcar_du_vgacon.h" /* ----------------------------------------------------------------------------- - * Common connector functions - */ - -struct drm_encoder * -rcar_du_connector_best_encoder(struct drm_connector *connector) -{ - struct rcar_du_connector *rcon = to_rcar_connector(connector); - - return rcar_encoder_to_drm_encoder(rcon->encoder); -} - -/* ----------------------------------------------------------------------------- * Encoder */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 719b6f2a031c..a8669c3e0dd5 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -49,9 +49,6 @@ struct rcar_du_connector { #define to_rcar_connector(c) \ container_of(c, struct rcar_du_connector, connector) -struct drm_encoder * -rcar_du_connector_best_encoder(struct drm_connector *connector); - int rcar_du_encoder_init(struct rcar_du_device *rcdu, enum rcar_du_encoder_type type, enum rcar_du_output output, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c index 6c927144b5c9..612b4d5ae098 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c @@ -52,7 +52,6 @@ static int rcar_du_hdmi_connector_mode_valid(struct drm_connector *connector, static const struct drm_connector_helper_funcs connector_helper_funcs = { .get_modes = rcar_du_hdmi_connector_get_modes, .mode_valid = rcar_du_hdmi_connector_mode_valid, - .best_encoder = rcar_du_connector_best_encoder, }; static enum drm_connector_status diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 86c109b16876..6bb032d8ac6b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -327,7 +327,7 @@ static int rcar_du_atomic_commit(struct drm_device *dev, } /* Swap the state, this is the point of no return. */ - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true); if (nonblock) schedule_work(&commit->work); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index e905f5da7aaa..6afd0af312ba 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -59,7 +59,6 @@ static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector) static const struct drm_connector_helper_funcs connector_helper_funcs = { .get_modes = rcar_du_lvds_connector_get_modes, - .best_encoder = rcar_du_connector_best_encoder, }; static enum drm_connector_status diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index 9d7e5c99caf6..8d6125c1c0f9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -28,7 +28,6 @@ static int rcar_du_vga_connector_get_modes(struct drm_connector *connector) static const struct drm_connector_helper_funcs connector_helper_funcs = { .get_modes = rcar_du_vga_connector_get_modes, - .best_encoder = rcar_du_connector_best_encoder, }; static enum drm_connector_status @@ -79,7 +78,5 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, if (ret < 0) return ret; - rcon->encoder = renc; - return 0; } diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 7f6a55cae27a..c120172add5c 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -349,20 +349,11 @@ static int rockchip_dp_remove(struct platform_device *pdev) return 0; } +static const struct dev_pm_ops rockchip_dp_pm_ops = { #ifdef CONFIG_PM_SLEEP -static int rockchip_dp_suspend(struct device *dev) -{ - return analogix_dp_suspend(dev); -} - -static int rockchip_dp_resume(struct device *dev) -{ - return analogix_dp_resume(dev); -} + .suspend = analogix_dp_suspend, + .resume_early = analogix_dp_resume, #endif - -static const struct dev_pm_ops rockchip_dp_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(rockchip_dp_suspend, rockchip_dp_resume) }; static const struct of_device_id rockchip_dp_dt_ids[] = { diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c index dedc65b40f36..ca22e5ee89ca 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c @@ -964,18 +964,9 @@ static enum drm_mode_status dw_mipi_dsi_mode_valid( return mode_status; } -static struct drm_encoder *dw_mipi_dsi_connector_best_encoder( - struct drm_connector *connector) -{ - struct dw_mipi_dsi *dsi = con_to_dsi(connector); - - return &dsi->encoder; -} - static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = { .get_modes = dw_mipi_dsi_connector_get_modes, .mode_valid = dw_mipi_dsi_mode_valid, - .best_encoder = dw_mipi_dsi_connector_best_encoder, }; static enum drm_connector_status diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c index f8b4feb60b25..006260de9dbd 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c @@ -579,14 +579,6 @@ inno_hdmi_connector_mode_valid(struct drm_connector *connector, return MODE_OK; } -static struct drm_encoder * -inno_hdmi_connector_best_encoder(struct drm_connector *connector) -{ - struct inno_hdmi *hdmi = to_inno_hdmi(connector); - - return &hdmi->encoder; -} - static int inno_hdmi_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY) @@ -613,7 +605,6 @@ static struct drm_connector_funcs inno_hdmi_connector_funcs = { static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = { .get_modes = inno_hdmi_connector_get_modes, .mode_valid = inno_hdmi_connector_mode_valid, - .best_encoder = inno_hdmi_connector_best_encoder, }; static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 09a4d429c0f0..c2bcc5ea1abe 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/of_graph.h> #include <linux/component.h> +#include <linux/console.h> #include "rockchip_drm_drv.h" #include "rockchip_drm_fb.h" @@ -38,6 +39,7 @@ #define DRIVER_MINOR 0 static bool is_support_iommu = true; +static struct drm_driver rockchip_drm_driver; /* * Attach a (component) device to the shared drm dma mapping from master drm @@ -133,20 +135,28 @@ static void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, priv->crtc_funcs[pipe]->disable_vblank(crtc); } -static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) +static int rockchip_drm_bind(struct device *dev) { + struct drm_device *drm_dev; struct rockchip_drm_private *private; struct dma_iommu_mapping *mapping = NULL; - struct device *dev = drm_dev->dev; - struct drm_connector *connector; int ret; - private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); - if (!private) + drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev); + if (!drm_dev) return -ENOMEM; - mutex_init(&private->commit.lock); - INIT_WORK(&private->commit.work, rockchip_drm_atomic_work); + ret = drm_dev_register(drm_dev, 0); + if (ret) + goto err_free; + + dev_set_drvdata(dev, drm_dev); + + private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); + if (!private) { + ret = -ENOMEM; + goto err_unregister; + } drm_dev->dev_private = private; @@ -187,21 +197,10 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) if (ret) goto err_detach_device; - /* - * All components are now added, we can publish the connector sysfs - * entries to userspace. This will generate hotplug events and so - * userspace will expect to be able to access DRM at this point. - */ - list_for_each_entry(connector, &drm_dev->mode_config.connector_list, - head) { - ret = drm_connector_register(connector); - if (ret) { - dev_err(drm_dev->dev, - "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n", - connector->base.id, - connector->name, ret); - goto err_unbind; - } + ret = drm_connector_register_all(drm_dev); + if (ret) { + dev_err(dev, "failed to register connectors\n"); + goto err_unbind; } /* init kms poll for handling hpd */ @@ -241,12 +240,16 @@ err_release_mapping: err_config_cleanup: drm_mode_config_cleanup(drm_dev); drm_dev->dev_private = NULL; +err_unregister: + drm_dev_unregister(drm_dev); +err_free: + drm_dev_unref(drm_dev); return ret; } -static int rockchip_drm_unload(struct drm_device *drm_dev) +static void rockchip_drm_unbind(struct device *dev) { - struct device *dev = drm_dev->dev; + struct drm_device *drm_dev = dev_get_drvdata(dev); rockchip_drm_fbdev_fini(drm_dev); drm_vblank_cleanup(drm_dev); @@ -256,29 +259,9 @@ static int rockchip_drm_unload(struct drm_device *drm_dev) arm_iommu_detach_device(dev); drm_mode_config_cleanup(drm_dev); drm_dev->dev_private = NULL; - - return 0; -} - -static void rockchip_drm_crtc_cancel_pending_vblank(struct drm_crtc *crtc, - struct drm_file *file_priv) -{ - struct rockchip_drm_private *priv = crtc->dev->dev_private; - int pipe = drm_crtc_index(crtc); - - if (pipe < ROCKCHIP_MAX_CRTC && - priv->crtc_funcs[pipe] && - priv->crtc_funcs[pipe]->cancel_pending_vblank) - priv->crtc_funcs[pipe]->cancel_pending_vblank(crtc, file_priv); -} - -static void rockchip_drm_preclose(struct drm_device *dev, - struct drm_file *file_priv) -{ - struct drm_crtc *crtc; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - rockchip_drm_crtc_cancel_pending_vblank(crtc, file_priv); + drm_dev_unregister(drm_dev); + drm_dev_unref(drm_dev); + dev_set_drvdata(dev, NULL); } void rockchip_drm_lastclose(struct drm_device *dev) @@ -304,9 +287,6 @@ static const struct file_operations rockchip_drm_driver_fops = { static struct drm_driver rockchip_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .load = rockchip_drm_load, - .unload = rockchip_drm_unload, - .preclose = rockchip_drm_preclose, .lastclose = rockchip_drm_lastclose, .get_vblank_counter = drm_vblank_no_hw_counter, .enable_vblank = rockchip_drm_crtc_enable_vblank, @@ -333,25 +313,38 @@ static struct drm_driver rockchip_drm_driver = { }; #ifdef CONFIG_PM_SLEEP -static int rockchip_drm_sys_suspend(struct device *dev) +void rockchip_drm_fb_suspend(struct drm_device *drm) { - struct drm_device *drm = dev_get_drvdata(dev); - struct drm_connector *connector; + struct rockchip_drm_private *priv = drm->dev_private; - if (!drm) - return 0; + console_lock(); + drm_fb_helper_set_suspend(&priv->fbdev_helper, 1); + console_unlock(); +} + +void rockchip_drm_fb_resume(struct drm_device *drm) +{ + struct rockchip_drm_private *priv = drm->dev_private; - drm_modeset_lock_all(drm); - list_for_each_entry(connector, &drm->mode_config.connector_list, head) { - int old_dpms = connector->dpms; + console_lock(); + drm_fb_helper_set_suspend(&priv->fbdev_helper, 0); + console_unlock(); +} + +static int rockchip_drm_sys_suspend(struct device *dev) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct rockchip_drm_private *priv = drm->dev_private; - if (connector->funcs->dpms) - connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF); + drm_kms_helper_poll_disable(drm); + rockchip_drm_fb_suspend(drm); - /* Set the old mode back to the connector for resume */ - connector->dpms = old_dpms; + priv->state = drm_atomic_helper_suspend(drm); + if (IS_ERR(priv->state)) { + rockchip_drm_fb_resume(drm); + drm_kms_helper_poll_enable(drm); + return PTR_ERR(priv->state); } - drm_modeset_unlock_all(drm); return 0; } @@ -359,47 +352,11 @@ static int rockchip_drm_sys_suspend(struct device *dev) static int rockchip_drm_sys_resume(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct drm_connector *connector; - enum drm_connector_status status; - bool changed = false; + struct rockchip_drm_private *priv = drm->dev_private; - if (!drm) - return 0; - - drm_modeset_lock_all(drm); - list_for_each_entry(connector, &drm->mode_config.connector_list, head) { - int desired_mode = connector->dpms; - - /* - * at suspend time, we save dpms to connector->dpms, - * restore the old_dpms, and at current time, the connector - * dpms status must be DRM_MODE_DPMS_OFF. - */ - connector->dpms = DRM_MODE_DPMS_OFF; - - /* - * If the connector has been disconnected during suspend, - * disconnect it from the encoder and leave it off. We'll notify - * userspace at the end. - */ - if (desired_mode == DRM_MODE_DPMS_ON) { - status = connector->funcs->detect(connector, true); - if (status == connector_status_disconnected) { - connector->encoder = NULL; - connector->status = status; - changed = true; - continue; - } - } - if (connector->funcs->dpms) - connector->funcs->dpms(connector, desired_mode); - } - drm_modeset_unlock_all(drm); - - drm_helper_resume_force_mode(drm); - - if (changed) - drm_kms_helper_hotplug_event(drm); + drm_atomic_helper_resume(drm, priv->state); + rockchip_drm_fb_resume(drm); + drm_kms_helper_poll_enable(drm); return 0; } @@ -440,37 +397,6 @@ static void rockchip_add_endpoints(struct device *dev, } } -static int rockchip_drm_bind(struct device *dev) -{ - struct drm_device *drm; - int ret; - - drm = drm_dev_alloc(&rockchip_drm_driver, dev); - if (!drm) - return -ENOMEM; - - ret = drm_dev_register(drm, 0); - if (ret) - goto err_free; - - dev_set_drvdata(dev, drm); - - return 0; - -err_free: - drm_dev_unref(drm); - return ret; -} - -static void rockchip_drm_unbind(struct device *dev) -{ - struct drm_device *drm = dev_get_drvdata(dev); - - drm_dev_unregister(drm); - drm_dev_unref(drm); - dev_set_drvdata(dev, NULL); -} - static const struct component_master_ops rockchip_drm_ops = { .bind = rockchip_drm_bind, .unbind = rockchip_drm_unbind, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 56f43a364c7f..ea3932940061 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -40,14 +40,6 @@ struct rockchip_crtc_funcs { int (*enable_vblank)(struct drm_crtc *crtc); void (*disable_vblank)(struct drm_crtc *crtc); void (*wait_for_update)(struct drm_crtc *crtc); - void (*cancel_pending_vblank)(struct drm_crtc *crtc, struct drm_file *file_priv); -}; - -struct rockchip_atomic_commit { - struct work_struct work; - struct drm_atomic_state *state; - struct drm_device *dev; - struct mutex lock; }; struct rockchip_crtc_state { @@ -68,11 +60,9 @@ struct rockchip_drm_private { struct drm_fb_helper fbdev_helper; struct drm_gem_object *fbdev_bo; const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC]; - - struct rockchip_atomic_commit commit; + struct drm_atomic_state *state; }; -void rockchip_drm_atomic_work(struct work_struct *work); int rockchip_register_crtc_funcs(struct drm_crtc *crtc, const struct rockchip_crtc_funcs *crtc_funcs); void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 755cfdba61cd..20f12bc5a386 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -228,87 +228,32 @@ rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_stat } static void -rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit) +rockchip_atomic_commit_tail(struct drm_atomic_state *state) { - struct drm_atomic_state *state = commit->state; - struct drm_device *dev = commit->dev; + struct drm_device *dev = state->dev; - /* - * TODO: do fence wait here. - */ - - /* - * Rockchip crtc support runtime PM, can't update display planes - * when crtc is disabled. - * - * drm_atomic_helper_commit comments detail that: - * For drivers supporting runtime PM the recommended sequence is - * - * drm_atomic_helper_commit_modeset_disables(dev, state); - * - * drm_atomic_helper_commit_modeset_enables(dev, state); - * - * drm_atomic_helper_commit_planes(dev, state, true); - * - * See the kerneldoc entries for these three functions for more details. - */ drm_atomic_helper_commit_modeset_disables(dev, state); drm_atomic_helper_commit_modeset_enables(dev, state); drm_atomic_helper_commit_planes(dev, state, true); + drm_atomic_helper_commit_hw_done(state); + rockchip_atomic_wait_for_complete(dev, state); drm_atomic_helper_cleanup_planes(dev, state); - - drm_atomic_state_free(state); -} - -void rockchip_drm_atomic_work(struct work_struct *work) -{ - struct rockchip_atomic_commit *commit = container_of(work, - struct rockchip_atomic_commit, work); - - rockchip_atomic_commit_complete(commit); } -int rockchip_drm_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, - bool nonblock) -{ - struct rockchip_drm_private *private = dev->dev_private; - struct rockchip_atomic_commit *commit = &private->commit; - int ret; - - ret = drm_atomic_helper_prepare_planes(dev, state); - if (ret) - return ret; - - /* serialize outstanding nonblocking commits */ - mutex_lock(&commit->lock); - flush_work(&commit->work); - - drm_atomic_helper_swap_state(dev, state); - - commit->dev = dev; - commit->state = state; - - if (nonblock) - schedule_work(&commit->work); - else - rockchip_atomic_commit_complete(commit); - - mutex_unlock(&commit->lock); - - return 0; -} +struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = { + .atomic_commit_tail = rockchip_atomic_commit_tail, +}; static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { .fb_create = rockchip_user_fb_create, .output_poll_changed = rockchip_drm_output_poll_changed, .atomic_check = drm_atomic_helper_check, - .atomic_commit = rockchip_drm_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, }; struct drm_framebuffer * @@ -339,4 +284,5 @@ void rockchip_drm_mode_config_init(struct drm_device *dev) dev->mode_config.max_height = 4096; dev->mode_config.funcs = &rockchip_drm_mode_config_funcs; + dev->mode_config.helper_private = &rockchip_mode_config_helpers; } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c index f261512bb4a0..207e01de6e32 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c @@ -108,7 +108,7 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, fbi->screen_size = rk_obj->base.size; fbi->fix.smem_len = rk_obj->base.size; - DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n", + DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%zu\n", fb->width, fb->height, fb->depth, rk_obj->kvaddr, offset, size); @@ -156,9 +156,6 @@ int rockchip_drm_fbdev_init(struct drm_device *dev) goto err_drm_fb_helper_fini; } - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); if (ret < 0) { dev_err(dev->dev, "Failed to set initial hw config - %d.\n", diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 9c2d8a894093..059e902f872d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -38,7 +38,7 @@ static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj, &rk_obj->dma_addr, GFP_KERNEL, &rk_obj->dma_attrs); if (!rk_obj->kvaddr) { - DRM_ERROR("failed to allocate %#x byte dma buffer", obj->size); + DRM_ERROR("failed to allocate %zu byte dma buffer", obj->size); return -ENOMEM; } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 5567fb43e674..8cd840f602b7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -98,7 +98,9 @@ struct vop_win { const struct vop_win_data *data; struct vop *vop; - struct vop_plane_state state; + /* protected by dev->event_lock */ + bool enable; + dma_addr_t yrgb_mst; }; struct vop { @@ -112,6 +114,8 @@ struct vop { bool vsync_work_pending; struct completion dsp_hold_completion; struct completion wait_update_complete; + + /* protected by dev->event_lock */ struct drm_pending_vblank_event *event; const struct vop_data *data; @@ -431,9 +435,6 @@ static void vop_enable(struct drm_crtc *crtc) struct vop *vop = to_vop(crtc); int ret; - if (vop->is_enabled) - return; - ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); @@ -501,8 +502,7 @@ static void vop_crtc_disable(struct drm_crtc *crtc) struct vop *vop = to_vop(crtc); int i; - if (!vop->is_enabled) - return; + WARN_ON(vop->event); /* * We need to make sure that all windows are disabled before we @@ -553,6 +553,14 @@ static void vop_crtc_disable(struct drm_crtc *crtc) clk_disable(vop->aclk); clk_disable(vop->hclk); pm_runtime_put(vop->dev); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } } static void vop_plane_destroy(struct drm_plane *plane) @@ -658,6 +666,11 @@ static void vop_plane_atomic_disable(struct drm_plane *plane, if (!old_state->crtc) return; + spin_lock_irq(&plane->dev->event_lock); + vop_win->enable = false; + vop_win->yrgb_mst = 0; + spin_unlock_irq(&plane->dev->event_lock); + spin_lock(&vop->reg_lock); VOP_WIN_SET(vop, win, enable, 0); @@ -692,7 +705,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, /* * can't update plane when vop is disabled. */ - if (!crtc) + if (WARN_ON(!crtc)) return; if (WARN_ON(!vop->is_enabled)) @@ -721,6 +734,11 @@ static void vop_plane_atomic_update(struct drm_plane *plane, offset += (src->y1 >> 16) * fb->pitches[0]; vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0]; + spin_lock_irq(&plane->dev->event_lock); + vop_win->enable = true; + vop_win->yrgb_mst = vop_plane_state->yrgb_mst; + spin_unlock_irq(&plane->dev->event_lock); + spin_lock(&vop->reg_lock); VOP_WIN_SET(vop, win, format, vop_plane_state->format); @@ -876,30 +894,10 @@ static void vop_crtc_wait_for_update(struct drm_crtc *crtc) WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100)); } -static void vop_crtc_cancel_pending_vblank(struct drm_crtc *crtc, - struct drm_file *file_priv) -{ - struct drm_device *drm = crtc->dev; - struct vop *vop = to_vop(crtc); - struct drm_pending_vblank_event *e; - unsigned long flags; - - spin_lock_irqsave(&drm->event_lock, flags); - e = vop->event; - if (e && e->base.file_priv == file_priv) { - vop->event = NULL; - - kfree(&e->base); - file_priv->event_space += sizeof(e->event); - } - spin_unlock_irqrestore(&drm->event_lock, flags); -} - static const struct rockchip_crtc_funcs private_crtc_funcs = { .enable_vblank = vop_crtc_enable_vblank, .disable_vblank = vop_crtc_disable_vblank, .wait_for_update = vop_crtc_wait_for_update, - .cancel_pending_vblank = vop_crtc_cancel_pending_vblank, }; static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, @@ -931,6 +929,8 @@ static void vop_crtc_enable(struct drm_crtc *crtc) u16 vact_end = vact_st + vdisplay; uint32_t val; + WARN_ON(vop->event); + vop_enable(crtc); /* * If dclk rate is zero, mean that scanout is stop, @@ -1027,12 +1027,15 @@ static void vop_crtc_atomic_begin(struct drm_crtc *crtc, { struct vop *vop = to_vop(crtc); + spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) { WARN_ON(drm_crtc_vblank_get(crtc) != 0); + WARN_ON(vop->event); vop->event = crtc->state->event; crtc->state->event = NULL; } + spin_unlock_irq(&crtc->dev->event_lock); } static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = { @@ -1080,16 +1083,14 @@ static const struct drm_crtc_funcs vop_crtc_funcs = { static bool vop_win_pending_is_complete(struct vop_win *vop_win) { - struct drm_plane *plane = &vop_win->base; - struct vop_plane_state *state = to_vop_plane_state(plane->state); dma_addr_t yrgb_mst; - if (!state->enable) + if (!vop_win->enable) return VOP_WIN_GET(vop_win->vop, vop_win->data, enable) == 0; yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win->data); - return yrgb_mst == state->yrgb_mst; + return yrgb_mst == vop_win->yrgb_mst; } static void vop_handle_vblank(struct vop *vop) @@ -1104,15 +1105,16 @@ static void vop_handle_vblank(struct vop *vop) return; } + spin_lock_irqsave(&drm->event_lock, flags); if (vop->event) { - spin_lock_irqsave(&drm->event_lock, flags); drm_crtc_send_vblank_event(crtc, vop->event); drm_crtc_vblank_put(crtc); vop->event = NULL; - spin_unlock_irqrestore(&drm->event_lock, flags); } + spin_unlock_irqrestore(&drm->event_lock, flags); + if (!completion_done(&vop->wait_update_complete)) complete(&vop->wait_update_complete); } diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index b440617a7019..dd2c400c4a46 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -215,7 +215,7 @@ static int sti_atomic_commit(struct drm_device *drm, * the software side now. */ - drm_atomic_helper_swap_state(drm, state); + drm_atomic_helper_swap_state(state, true); if (nonblock) sti_atomic_schedule(private, state); diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index d439128e6309..e2901667eceb 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -377,20 +377,10 @@ static int sti_dvo_connector_mode_valid(struct drm_connector *connector, return MODE_OK; } -struct drm_encoder *sti_dvo_best_encoder(struct drm_connector *connector) -{ - struct sti_dvo_connector *dvo_connector - = to_sti_dvo_connector(connector); - - /* Best encoder is the one associated during connector creation */ - return dvo_connector->encoder; -} - static const struct drm_connector_helper_funcs sti_dvo_connector_helper_funcs = { .get_modes = sti_dvo_connector_get_modes, .mode_valid = sti_dvo_connector_mode_valid, - .best_encoder = sti_dvo_best_encoder, }; static enum drm_connector_status diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 9f49c00f1a02..dcec5a8eda59 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -669,20 +669,10 @@ static int sti_hda_connector_mode_valid(struct drm_connector *connector, return MODE_OK; } -struct drm_encoder *sti_hda_best_encoder(struct drm_connector *connector) -{ - struct sti_hda_connector *hda_connector - = to_sti_hda_connector(connector); - - /* Best encoder is the one associated during connector creation */ - return hda_connector->encoder; -} - static const struct drm_connector_helper_funcs sti_hda_connector_helper_funcs = { .get_modes = sti_hda_connector_get_modes, .mode_valid = sti_hda_connector_mode_valid, - .best_encoder = sti_hda_best_encoder, }; static enum drm_connector_status diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 85545ebf88d3..36d9d6635784 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -890,20 +890,10 @@ static int sti_hdmi_connector_mode_valid(struct drm_connector *connector, return MODE_OK; } -struct drm_encoder *sti_hdmi_best_encoder(struct drm_connector *connector) -{ - struct sti_hdmi_connector *hdmi_connector - = to_sti_hdmi_connector(connector); - - /* Best encoder is the one associated during connector creation */ - return hdmi_connector->encoder; -} - static const struct drm_connector_helper_funcs sti_hdmi_connector_helper_funcs = { .get_modes = sti_hdmi_connector_get_modes, .mode_valid = sti_hdmi_connector_mode_valid, - .best_encoder = sti_hdmi_best_encoder, }; /* get detection status of display device */ diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c index 4182a21f5923..f628b6d8f23f 100644 --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c @@ -51,10 +51,22 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc, { struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); struct sun4i_drv *drv = scrtc->drv; + struct drm_pending_vblank_event *event = crtc->state->event; DRM_DEBUG_DRIVER("Committing plane changes\n"); sun4i_backend_commit(drv->backend); + + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } } static void sun4i_crtc_disable(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index ab6494818050..442cfe271688 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -90,19 +90,9 @@ static int sun4i_rgb_mode_valid(struct drm_connector *connector, return MODE_OK; } -static struct drm_encoder * -sun4i_rgb_best_encoder(struct drm_connector *connector) -{ - struct sun4i_rgb *rgb = - drm_connector_to_sun4i_rgb(connector); - - return &rgb->encoder; -} - static struct drm_connector_helper_funcs sun4i_rgb_con_helper_funcs = { .get_modes = sun4i_rgb_get_modes, .mode_valid = sun4i_rgb_mode_valid, - .best_encoder = sun4i_rgb_best_encoder, }; static enum drm_connector_status diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c index bc047f923508..b84147896294 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tv.c +++ b/drivers/gpu/drm/sun4i/sun4i_tv.c @@ -526,18 +526,9 @@ static int sun4i_tv_comp_mode_valid(struct drm_connector *connector, return MODE_OK; } -static struct drm_encoder * -sun4i_tv_comp_best_encoder(struct drm_connector *connector) -{ - struct sun4i_tv *tv = drm_connector_to_sun4i_tv(connector); - - return &tv->encoder; -} - static struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = { .get_modes = sun4i_tv_comp_get_modes, .mode_valid = sun4i_tv_comp_mode_valid, - .best_encoder = sun4i_tv_comp_best_encoder, }; static enum drm_connector_status diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index b59c3bf0df44..a177a42a9849 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -93,7 +93,7 @@ static int tegra_atomic_commit(struct drm_device *drm, * the software side now. */ - drm_atomic_helper_swap_state(drm, state); + drm_atomic_helper_swap_state(state, true); if (nonblock) tegra_atomic_schedule(tegra, state); diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index f52d6cb24ff5..0ddcce1b420d 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -239,8 +239,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output); void tegra_output_exit(struct tegra_output *output); int tegra_output_connector_get_modes(struct drm_connector *connector); -struct drm_encoder * -tegra_output_connector_best_encoder(struct drm_connector *connector); enum drm_connector_status tegra_output_connector_detect(struct drm_connector *connector, bool force); void tegra_output_connector_destroy(struct drm_connector *connector); diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index d1239ebc190f..099cccb2fbcb 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -794,7 +794,6 @@ tegra_dsi_connector_mode_valid(struct drm_connector *connector, static const struct drm_connector_helper_funcs tegra_dsi_connector_helper_funcs = { .get_modes = tegra_output_connector_get_modes, .mode_valid = tegra_dsi_connector_mode_valid, - .best_encoder = tegra_output_connector_best_encoder, }; static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = { diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index b7ef4929e347..2fdb8796443e 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -806,7 +806,6 @@ static const struct drm_connector_helper_funcs tegra_hdmi_connector_helper_funcs = { .get_modes = tegra_output_connector_get_modes, .mode_valid = tegra_hdmi_connector_mode_valid, - .best_encoder = tegra_output_connector_best_encoder, }; static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = { diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 46664b622270..1480f6aaffe4 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -42,14 +42,6 @@ int tegra_output_connector_get_modes(struct drm_connector *connector) return err; } -struct drm_encoder * -tegra_output_connector_best_encoder(struct drm_connector *connector) -{ - struct tegra_output *output = connector_to_output(connector); - - return &output->encoder; -} - enum drm_connector_status tegra_output_connector_detect(struct drm_connector *connector, bool force) { diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index e246334e0252..a131b44e2d6f 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -112,7 +112,6 @@ tegra_rgb_connector_mode_valid(struct drm_connector *connector, static const struct drm_connector_helper_funcs tegra_rgb_connector_helper_funcs = { .get_modes = tegra_output_connector_get_modes, .mode_valid = tegra_rgb_connector_mode_valid, - .best_encoder = tegra_output_connector_best_encoder, }; static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = { diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 757c6e8603af..34958d71284b 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1087,7 +1087,6 @@ tegra_sor_connector_mode_valid(struct drm_connector *connector, static const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = { .get_modes = tegra_sor_connector_get_modes, .mode_valid = tegra_sor_connector_mode_valid, - .best_encoder = tegra_output_connector_best_encoder, }; static const struct drm_encoder_funcs tegra_sor_encoder_funcs = { diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 9817dbfa4ac3..dba1114297e4 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -208,14 +208,6 @@ static int vc4_dpi_connector_get_modes(struct drm_connector *connector) return 0; } -static struct drm_encoder * -vc4_dpi_connector_best_encoder(struct drm_connector *connector) -{ - struct vc4_dpi_connector *dpi_connector = - to_vc4_dpi_connector(connector); - return dpi_connector->encoder; -} - static const struct drm_connector_funcs vc4_dpi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = vc4_dpi_connector_detect, @@ -228,7 +220,6 @@ static const struct drm_connector_funcs vc4_dpi_connector_funcs = { static const struct drm_connector_helper_funcs vc4_dpi_connector_helper_funcs = { .get_modes = vc4_dpi_connector_get_modes, - .best_encoder = vc4_dpi_connector_best_encoder, }; static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev, diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index fd2644d231ff..68df91c3f860 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -208,14 +208,6 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) return ret; } -static struct drm_encoder * -vc4_hdmi_connector_best_encoder(struct drm_connector *connector) -{ - struct vc4_hdmi_connector *hdmi_connector = - to_vc4_hdmi_connector(connector); - return hdmi_connector->encoder; -} - static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = vc4_hdmi_connector_detect, @@ -228,7 +220,6 @@ static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = { .get_modes = vc4_hdmi_connector_get_modes, - .best_encoder = vc4_hdmi_connector_best_encoder, }; static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev, diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 39c0b2048bfd..8f4d5ffc32be 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -148,7 +148,7 @@ static int vc4_atomic_commit(struct drm_device *dev, * the software side now. */ - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true); /* * Everything below can be run asynchronously without the need to grab diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index d82ae1cddfcf..ac758cdbc1bc 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -38,56 +38,11 @@ #define XRES_MAX 8192 #define YRES_MAX 8192 -static int virtio_gpu_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t flags) -{ - struct virtio_gpu_device *vgdev = crtc->dev->dev_private; - struct virtio_gpu_output *output = - container_of(crtc, struct virtio_gpu_output, crtc); - struct drm_plane *plane = crtc->primary; - struct virtio_gpu_framebuffer *vgfb; - struct virtio_gpu_object *bo; - unsigned long irqflags; - uint32_t handle; - - plane->fb = fb; - vgfb = to_virtio_gpu_framebuffer(plane->fb); - bo = gem_to_virtio_gpu_obj(vgfb->obj); - handle = bo->hw_res_handle; - - DRM_DEBUG("handle 0x%x%s, crtc %dx%d\n", handle, - bo->dumb ? ", dumb" : "", - crtc->mode.hdisplay, crtc->mode.vdisplay); - if (bo->dumb) { - virtio_gpu_cmd_transfer_to_host_2d - (vgdev, handle, 0, - cpu_to_le32(crtc->mode.hdisplay), - cpu_to_le32(crtc->mode.vdisplay), - 0, 0, NULL); - } - virtio_gpu_cmd_set_scanout(vgdev, output->index, handle, - crtc->mode.hdisplay, - crtc->mode.vdisplay, 0, 0); - virtio_gpu_cmd_resource_flush(vgdev, handle, 0, 0, - crtc->mode.hdisplay, - crtc->mode.vdisplay); - - if (event) { - spin_lock_irqsave(&crtc->dev->event_lock, irqflags); - drm_crtc_send_vblank_event(crtc, event); - spin_unlock_irqrestore(&crtc->dev->event_lock, irqflags); - } - - return 0; -} - static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .destroy = drm_crtc_cleanup, - .page_flip = virtio_gpu_page_flip, + .page_flip = drm_atomic_helper_page_flip, .reset = drm_atomic_helper_crtc_reset, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, @@ -185,6 +140,7 @@ static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc, spin_lock_irqsave(&crtc->dev->event_lock, flags); if (crtc->state->event) drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; spin_unlock_irqrestore(&crtc->dev->event_lock, flags); } @@ -259,15 +215,6 @@ static int virtio_gpu_conn_mode_valid(struct drm_connector *connector, return MODE_BAD; } -static struct drm_encoder* -virtio_gpu_best_encoder(struct drm_connector *connector) -{ - struct virtio_gpu_output *virtio_gpu_output = - drm_connector_to_virtio_gpu_output(connector); - - return &virtio_gpu_output->enc; -} - static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = { .mode_set = virtio_gpu_enc_mode_set, .enable = virtio_gpu_enc_enable, @@ -277,7 +224,6 @@ static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = { static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = { .get_modes = virtio_gpu_conn_get_modes, .mode_valid = virtio_gpu_conn_mode_valid, - .best_encoder = virtio_gpu_best_encoder, }; static enum drm_connector_status virtio_gpu_conn_detect( @@ -388,30 +334,28 @@ virtio_gpu_user_framebuffer_create(struct drm_device *dev, return &virtio_gpu_fb->base; } -static int vgdev_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, - bool nonblock) +static void vgdev_atomic_commit_tail(struct drm_atomic_state *state) { - if (nonblock) - return -EBUSY; - - drm_atomic_helper_swap_state(dev, state); - drm_atomic_helper_wait_for_fences(dev, state); + struct drm_device *dev = state->dev; drm_atomic_helper_commit_modeset_disables(dev, state); drm_atomic_helper_commit_modeset_enables(dev, state); drm_atomic_helper_commit_planes(dev, state, true); + drm_atomic_helper_commit_hw_done(state); + drm_atomic_helper_wait_for_vblanks(dev, state); drm_atomic_helper_cleanup_planes(dev, state); - drm_atomic_state_free(state); - return 0; } +struct drm_mode_config_helper_funcs virtio_mode_config_helpers = { + .atomic_commit_tail = vgdev_atomic_commit_tail, +}; + static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = { .fb_create = virtio_gpu_user_framebuffer_create, .atomic_check = drm_atomic_helper_check, - .atomic_commit = vgdev_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, }; int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) @@ -419,7 +363,8 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) int i; drm_mode_config_init(vgdev->ddev); - vgdev->ddev->mode_config.funcs = (void *)&virtio_gpu_mode_funcs; + vgdev->ddev->mode_config.funcs = &virtio_gpu_mode_funcs; + vgdev->ddev->mode_config.helper_private = &virtio_mode_config_helpers; /* modes will be validated against the framebuffer size */ vgdev->ddev->mode_config.min_width = XRES_MIN; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c index 67cebb23c940..aa04fb0159a7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c @@ -293,13 +293,10 @@ static int vmw_cmdbuf_header_submit(struct vmw_cmdbuf_header *header) struct vmw_cmdbuf_man *man = header->man; u32 val; - if (sizeof(header->handle) > 4) - val = (header->handle >> 32); - else - val = 0; + val = upper_32_bits(header->handle); vmw_write(man->dev_priv, SVGA_REG_COMMAND_HIGH, val); - val = (header->handle & 0xFFFFFFFFULL); + val = lower_32_bits(header->handle); val |= header->cb_context & SVGA_CB_CONTEXT_MASK; vmw_write(man->dev_priv, SVGA_REG_COMMAND_LOW, val); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 9e5eefd6f733..04310cb08111 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -68,6 +68,7 @@ #include <drm/drm_agpsupport.h> #include <drm/drm_crtc.h> +#include <drm/drm_fourcc.h> #include <drm/drm_global.h> #include <drm/drm_hashtab.h> #include <drm/drm_mem_util.h> @@ -283,6 +284,7 @@ struct drm_ioctl_desc { /* Event queued up for userspace to read */ struct drm_pending_event { + struct completion *completion; struct drm_event *event; struct fence *fence; struct list_head link; @@ -417,8 +419,6 @@ struct drm_driver { void (*postclose) (struct drm_device *, struct drm_file *); void (*lastclose) (struct drm_device *); int (*unload) (struct drm_device *); - int (*suspend) (struct drm_device *, pm_message_t state); - int (*resume) (struct drm_device *); int (*dma_ioctl) (struct drm_device *dev, void *data, struct drm_file *file_priv); int (*dma_quiescent) (struct drm_device *); int (*context_dtor) (struct drm_device *dev, int context); @@ -969,18 +969,12 @@ extern u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, struct timeval *vblanktime); extern u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, struct timeval *vblanktime); -extern void drm_send_vblank_event(struct drm_device *dev, unsigned int pipe, - struct drm_pending_vblank_event *e); extern void drm_crtc_send_vblank_event(struct drm_crtc *crtc, struct drm_pending_vblank_event *e); -extern void drm_arm_vblank_event(struct drm_device *dev, unsigned int pipe, - struct drm_pending_vblank_event *e); extern void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, struct drm_pending_vblank_event *e); extern bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe); extern bool drm_crtc_handle_vblank(struct drm_crtc *crtc); -extern int drm_vblank_get(struct drm_device *dev, unsigned int pipe); -extern void drm_vblank_put(struct drm_device *dev, unsigned int pipe); extern int drm_crtc_vblank_get(struct drm_crtc *crtc); extern void drm_crtc_vblank_put(struct drm_crtc *crtc); extern void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index d12cfb9c6062..856a9c85a838 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -30,6 +30,12 @@ #include <drm/drm_crtc.h> +void drm_crtc_commit_put(struct drm_crtc_commit *commit); +static inline void drm_crtc_commit_get(struct drm_crtc_commit *commit) +{ + kref_get(&commit->ref); +} + struct drm_atomic_state * __must_check drm_atomic_state_alloc(struct drm_device *dev); void drm_atomic_state_clear(struct drm_atomic_state *state); @@ -198,6 +204,16 @@ int __must_check drm_atomic_nonblocking_commit(struct drm_atomic_state *state); (plane_state) = (__state)->planes[__i].state, 1); \ (__i)++) \ for_each_if (plane_state) + +/** + * drm_atomic_crtc_needs_modeset - compute combined modeset need + * @state: &drm_crtc_state for the CRTC + * + * To give drivers flexibility struct &drm_crtc_state has 3 booleans to track + * whether the state CRTC changed enough to need a full modeset cycle: + * connectors_changed, mode_changed and active_change. This helper simply + * combines these three to compute the overall need for a modeset for @state. + */ static inline bool drm_atomic_crtc_needs_modeset(struct drm_crtc_state *state) { diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 1877a7c18d8e..d86ae5dcd7b4 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -38,6 +38,7 @@ int drm_atomic_helper_check_planes(struct drm_device *dev, struct drm_atomic_state *state); int drm_atomic_helper_check(struct drm_device *dev, struct drm_atomic_state *state); +void drm_atomic_helper_commit_tail(struct drm_atomic_state *state); int drm_atomic_helper_commit(struct drm_device *dev, struct drm_atomic_state *state, bool nonblock); @@ -71,8 +72,15 @@ void drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_sta void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, bool atomic); -void drm_atomic_helper_swap_state(struct drm_device *dev, - struct drm_atomic_state *state); +void drm_atomic_helper_swap_state(struct drm_atomic_state *state, + bool stall); + +/* nonblocking commit helpers */ +int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, + bool nonblock); +void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state); +void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state); +void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state); /* implementations for legacy interfaces */ int drm_atomic_helper_update_plane(struct drm_plane *plane, @@ -159,7 +167,7 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, * This iterates over the current state, useful (for example) when applying * atomic state after it has been checked and swapped. To iterate over the * planes which *will* be attached (for ->atomic_check()) see - * drm_crtc_for_each_pending_plane(). + * drm_atomic_crtc_state_for_each_plane(). */ #define drm_atomic_crtc_for_each_plane(plane, crtc) \ drm_for_each_plane_mask(plane, (crtc)->dev, (crtc)->state->plane_mask) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 1a8d66ca677c..914baa8c161d 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -728,9 +728,6 @@ struct drm_crtc_funcs { * @gamma_store: gamma ramp values * @helper_private: mid-layer private data * @properties: property tracking for this CRTC - * @state: current atomic state for this CRTC - * @acquire_ctx: per-CRTC implicit acquire context used by atomic drivers for - * legacy IOCTLs * * Each CRTC may have one or more connectors associated with it. This structure * allows the CRTC to be controlled. @@ -787,11 +784,37 @@ struct drm_crtc { struct drm_object_properties properties; + /** + * @state: + * + * Current atomic state for this CRTC. + */ struct drm_crtc_state *state; - /* - * For legacy crtc IOCTLs so that atomic drivers can get at the locking - * acquire context. + /** + * @commit_list: + * + * List of &drm_crtc_commit structures tracking pending commits. + * Protected by @commit_lock. This list doesn't hold its own full + * reference, but burrows it from the ongoing commit. Commit entries + * must be removed from this list once the commit is fully completed, + * but before it's correspoding &drm_atomic_state gets destroyed. + */ + struct list_head commit_list; + + /** + * @commit_lock: + * + * Spinlock to protect @commit_list. + */ + spinlock_t commit_lock; + + /** + * @acquire_ctx: + * + * Per-CRTC implicit acquire context used by atomic drivers for legacy + * IOCTLs, so that atomic drivers can get at the locking acquire + * context. */ struct drm_modeset_acquire_ctx *acquire_ctx; }; @@ -1733,6 +1756,111 @@ struct drm_bridge { void *driver_private; }; +/** + * struct drm_crtc_commit - track modeset commits on a CRTC + * + * This structure is used to track pending modeset changes and atomic commit on + * a per-CRTC basis. Since updating the list should never block this structure + * is reference counted to allow waiters to safely wait on an event to complete, + * without holding any locks. + * + * It has 3 different events in total to allow a fine-grained synchronization + * between outstanding updates:: + * + * atomic commit thread hardware + * + * write new state into hardware ----> ... + * signal hw_done + * switch to new state on next + * ... v/hblank + * + * wait for buffers to show up ... + * + * ... send completion irq + * irq handler signals flip_done + * cleanup old buffers + * + * signal cleanup_done + * + * wait for flip_done <---- + * clean up atomic state + * + * The important bit to know is that cleanup_done is the terminal event, but the + * ordering between flip_done and hw_done is entirely up to the specific driver + * and modeset state change. + * + * For an implementation of how to use this look at + * drm_atomic_helper_setup_commit() from the atomic helper library. + */ +struct drm_crtc_commit { + /** + * @crtc: + * + * DRM CRTC for this commit. + */ + struct drm_crtc *crtc; + + /** + * @ref: + * + * Reference count for this structure. Needed to allow blocking on + * completions without the risk of the completion disappearing + * meanwhile. + */ + struct kref ref; + + /** + * @flip_done: + * + * Will be signaled when the hardware has flipped to the new set of + * buffers. Signals at the same time as when the drm event for this + * commit is sent to userspace, or when an out-fence is singalled. Note + * that for most hardware, in most cases this happens after @hw_done is + * signalled. + */ + struct completion flip_done; + + /** + * @hw_done: + * + * Will be signalled when all hw register changes for this commit have + * been written out. Especially when disabling a pipe this can be much + * later than than @flip_done, since that can signal already when the + * screen goes black, whereas to fully shut down a pipe more register + * I/O is required. + * + * Note that this does not need to include separately reference-counted + * resources like backing storage buffer pinning, or runtime pm + * management. + */ + struct completion hw_done; + + /** + * @cleanup_done: + * + * Will be signalled after old buffers have been cleaned up by calling + * drm_atomic_helper_cleanup_planes(). Since this can only happen after + * a vblank wait completed it might be a bit later. This completion is + * useful to throttle updates and avoid hardware updates getting ahead + * of the buffer cleanup too much. + */ + struct completion cleanup_done; + + /** + * @commit_entry: + * + * Entry on the per-CRTC commit_list. Protected by crtc->commit_lock. + */ + struct list_head commit_entry; + + /** + * @event: + * + * &drm_pending_vblank_event pointer to clean up private events. + */ + struct drm_pending_vblank_event *event; +}; + struct __drm_planes_state { struct drm_plane *ptr; struct drm_plane_state *state; @@ -1741,6 +1869,7 @@ struct __drm_planes_state { struct __drm_crtcs_state { struct drm_crtc *ptr; struct drm_crtc_state *state; + struct drm_crtc_commit *commit; }; struct __drm_connnectors_state { @@ -1771,6 +1900,14 @@ struct drm_atomic_state { struct __drm_connnectors_state *connectors; struct drm_modeset_acquire_ctx *acquire_ctx; + + /** + * @commit_work: + * + * Work item which can be used by the driver or helpers to execute the + * commit without blocking. + */ + struct work_struct commit_work; }; @@ -2111,6 +2248,7 @@ struct drm_mode_config_funcs { * @async_page_flip: does this device support async flips on the primary plane? * @cursor_width: hint to userspace for max cursor width * @cursor_height: hint to userspace for max cursor height + * @helper_private: mid-layer private data * * Core mode resource tracking structure. All CRTC, encoders, and connectors * enumerated by the driver are added here, as are global properties. Some @@ -2254,6 +2392,8 @@ struct drm_mode_config { /* cursor size */ uint32_t cursor_width, cursor_height; + + struct drm_mode_config_helper_funcs *helper_private; }; /** @@ -2645,15 +2785,6 @@ extern int drm_mode_plane_set_obj_prop(struct drm_plane *plane, extern int drm_mode_atomic_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, - int *bpp); -extern int drm_format_num_planes(uint32_t format); -extern int drm_format_plane_cpp(uint32_t format, int plane); -extern int drm_format_horz_chroma_subsampling(uint32_t format); -extern int drm_format_vert_chroma_subsampling(uint32_t format); -extern int drm_format_plane_width(int width, uint32_t format, int plane); -extern int drm_format_plane_height(int height, uint32_t format, int plane); -extern const char *drm_get_format_name(uint32_t format); extern struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, unsigned int supported_rotations); extern unsigned int drm_rotation_simplify(unsigned int rotation, diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 5b4aa35026a3..db8d4780eaa2 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -212,17 +212,6 @@ struct drm_fb_helper { * needs to be reprobe when fbdev is in control again. */ bool delayed_hotplug; - - /** - * @atomic: - * - * Use atomic updates for restore_fbdev_mode(), etc. This defaults to - * true if driver has DRIVER_ATOMIC feature flag, but drivers can - * override it to true after drm_fb_helper_init() if they support atomic - * modeset but do not yet advertise DRIVER_ATOMIC (note that fb-helper - * does not require ASYNC commits). - */ - bool atomic; }; #ifdef CONFIG_DRM_FBDEV_EMULATION diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h new file mode 100644 index 000000000000..7f90a396cf2b --- /dev/null +++ b/include/drm/drm_fourcc.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ +#ifndef __DRM_FOURCC_H__ +#define __DRM_FOURCC_H__ + +#include <linux/types.h> +#include <uapi/drm/drm_fourcc.h> + +void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp); +int drm_format_num_planes(uint32_t format); +int drm_format_plane_cpp(uint32_t format, int plane); +int drm_format_horz_chroma_subsampling(uint32_t format); +int drm_format_vert_chroma_subsampling(uint32_t format); +int drm_format_plane_width(int width, uint32_t format, int plane); +int drm_format_plane_height(int height, uint32_t format, int plane); +const char *drm_get_format_name(uint32_t format); + +#endif /* __DRM_FOURCC_H__ */ diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index ec552854a8f8..72f5b15e0738 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -180,6 +180,8 @@ struct mipi_dsi_device { unsigned long mode_flags; }; +#define MIPI_DSI_MODULE_PREFIX "mipi-dsi:" + static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev) { return container_of(dev, struct mipi_dsi_device, dev); diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 4e7a53b12632..b55f21857a98 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -931,4 +931,43 @@ static inline void drm_plane_helper_add(struct drm_plane *plane, plane->helper_private = funcs; } +/** + * struct drm_mode_config_helper_funcs - global modeset helper operations + * + * These helper functions are used by the atomic helpers. + */ +struct drm_mode_config_helper_funcs { + /** + * @atomic_commit_tail: + * + * This hook is used by the default atomic_commit() hook implemented in + * drm_atomic_helper_commit() together with the nonblocking commit + * helpers (see drm_atomic_helper_setup_commit() for a starting point) + * to implement blocking and nonblocking commits easily. It is not used + * by the atomic helpers + * + * This hook should first commit the given atomic state to the hardware. + * But drivers can add more waiting calls at the start of their + * implementation, e.g. to wait for driver-internal request for implicit + * syncing, before starting to commit the update to the hardware. + * + * After the atomic update is committed to the hardware this hook needs + * to call drm_atomic_helper_commit_hw_done(). Then wait for the upate + * to be executed by the hardware, for example using + * drm_atomic_helper_wait_for_vblanks(), and then clean up the old + * framebuffers using drm_atomic_helper_cleanup_planes(). + * + * When disabling a CRTC this hook _must_ stall for the commit to + * complete. Vblank waits don't work on disabled CRTC, hence the core + * can't take care of this. And it also can't rely on the vblank event, + * since that can be signalled already when the screen shows black, + * which can happen much earlier than the last hardware access needed to + * shut off the display pipeline completely. + * + * This hook is optional, the default implementation is + * drm_atomic_helper_commit_tail(). + */ + void (*atomic_commit_tail)(struct drm_atomic_state *state); +}; + #endif diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h new file mode 100644 index 000000000000..269039722f91 --- /dev/null +++ b/include/drm/drm_simple_kms_helper.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_DRM_SIMPLE_KMS_HELPER_H +#define __LINUX_DRM_SIMPLE_KMS_HELPER_H + +struct drm_simple_display_pipe; + +/** + * struct drm_simple_display_pipe_funcs - helper operations for a simple + * display pipeline + */ +struct drm_simple_display_pipe_funcs { + /** + * @enable: + * + * This function should be used to enable the pipeline. + * It is called when the underlying crtc is enabled. + * This hook is optional. + */ + void (*enable)(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state); + /** + * @disable: + * + * This function should be used to disable the pipeline. + * It is called when the underlying crtc is disabled. + * This hook is optional. + */ + void (*disable)(struct drm_simple_display_pipe *pipe); + + /** + * @check: + * + * This function is called in the check phase of an atomic update, + * specifically when the underlying plane is checked. + * The simple display pipeline helpers already check that the plane is + * not scaled, fills the entire visible area and is always enabled + * when the crtc is also enabled. + * This hook is optional. + * + * RETURNS: + * + * 0 on success, -EINVAL if the state or the transition can't be + * supported, -ENOMEM on memory allocation failure and -EDEADLK if an + * attempt to obtain another state object ran into a &drm_modeset_lock + * deadlock. + */ + int (*check)(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state, + struct drm_crtc_state *crtc_state); + /** + * @update: + * + * This function is called when the underlying plane state is updated. + * This hook is optional. + */ + void (*update)(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); +}; + +/** + * struct drm_simple_display_pipe - simple display pipeline + * @crtc: CRTC control structure + * @plane: Plane control structure + * @encoder: Encoder control structure + * @connector: Connector control structure + * @funcs: Pipeline control functions (optional) + * + * Simple display pipeline with plane, crtc and encoder collapsed into one + * entity. It should be initialized by calling drm_simple_display_pipe_init(). + */ +struct drm_simple_display_pipe { + struct drm_crtc crtc; + struct drm_plane plane; + struct drm_encoder encoder; + struct drm_connector *connector; + + const struct drm_simple_display_pipe_funcs *funcs; +}; + +int drm_simple_display_pipe_init(struct drm_device *dev, + struct drm_simple_display_pipe *pipe, + const struct drm_simple_display_pipe_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + struct drm_connector *connector); + +#endif /* __LINUX_DRM_SIMPLE_KMS_HELPER_H */ diff --git a/scripts/kernel-doc b/scripts/kernel-doc index 2fc8fad5195e..27757c21551a 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -59,6 +59,12 @@ Output format selection (mutually exclusive): -text Output plain text format. Output selection (mutually exclusive): + -export Only output documentation for symbols that have been + exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() + in the same FILE. + -internal Only output documentation for symbols that have NOT been + exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() + in the same FILE. -function NAME Only output documentation for the given function(s) or DOC: section title(s). All other functions and DOC: sections are ignored. May be specified multiple times. @@ -68,6 +74,8 @@ Output selection (mutually exclusive): Output selection modifiers: -no-doc-sections Do not output DOC: sections. + -enable-lineno Enable output of #define LINENO lines. Only works with + reStructuredText format. Other parameters: -v Verbose output, more warnings and other information. @@ -206,6 +214,10 @@ my $type_struct_xml = '\\&((struct\s*)*[_\w]+)'; my $type_env = '(\$\w+)'; my $type_enum_full = '\&(enum)\s*([_\w]+)'; my $type_struct_full = '\&(struct)\s*([_\w]+)'; +my $type_typedef_full = '\&(typedef)\s*([_\w]+)'; +my $type_union_full = '\&(union)\s*([_\w]+)'; +my $type_member = '\&([_\w]+)((\.|->)[_\w]+)'; +my $type_member_func = $type_member . '\(\)'; # Output conversion substitutions. # One for each output format @@ -274,10 +286,16 @@ my $blankline_text = ""; # rst-mode my @highlights_rst = ( [$type_constant, "``\$1``"], - [$type_func, "\\:c\\:func\\:`\$1`"], + # Note: need to escape () to avoid func matching later + [$type_member_func, "\\:c\\:type\\:`\$1\$2\\\\(\\\\) <\$1>`"], + [$type_member, "\\:c\\:type\\:`\$1\$2 <\$1>`"], + [$type_func, "\\:c\\:func\\:`\$1()`"], [$type_struct_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], [$type_enum_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], - [$type_struct, "\\:c\\:type\\:`struct \$1 <\$1>`"], + [$type_typedef_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], + [$type_union_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], + # in rst this can refer to any type + [$type_struct, "\\:c\\:type\\:`\$1`"], [$type_param, "**\$1**"] ); my $blankline_rst = "\n"; @@ -303,10 +321,19 @@ my $verbose = 0; my $output_mode = "man"; my $output_preformatted = 0; my $no_doc_sections = 0; +my $enable_lineno = 0; my @highlights = @highlights_man; my $blankline = $blankline_man; my $modulename = "Kernel API"; -my $function_only = 0; + +use constant { + OUTPUT_ALL => 0, # output all symbols and doc sections + OUTPUT_INCLUDE => 1, # output only specified symbols + OUTPUT_EXCLUDE => 2, # output everything except specified symbols + OUTPUT_EXPORTED => 3, # output exported symbols + OUTPUT_INTERNAL => 4, # output non-exported symbols +}; +my $output_selection = OUTPUT_ALL; my $show_not_found = 0; my @build_time; @@ -327,6 +354,7 @@ my $man_date = ('January', 'February', 'March', 'April', 'May', 'June', # CAVEAT EMPTOR! Some of the others I localised may not want to be, which # could cause "use of undefined value" or other bugs. my ($function, %function_table, %parametertypes, $declaration_purpose); +my $declaration_start_line; my ($type, $declaration_name, $return_type); my ($newsection, $newcontents, $prototype, $brcount, %source_map); @@ -344,52 +372,62 @@ my $section_counter = 0; my $lineprefix=""; -# states -# 0 - normal code -# 1 - looking for function name -# 2 - scanning field start. -# 3 - scanning prototype. -# 4 - documentation block -# 5 - gathering documentation outside main block +# Parser states +use constant { + STATE_NORMAL => 0, # normal code + STATE_NAME => 1, # looking for function name + STATE_FIELD => 2, # scanning field start + STATE_PROTO => 3, # scanning prototype + STATE_DOCBLOCK => 4, # documentation block + STATE_INLINE => 5, # gathering documentation outside main block +}; my $state; my $in_doc_sect; -# Split Doc State -# 0 - Invalid (Before start or after finish) -# 1 - Is started (the /** was found inside a struct) -# 2 - The @parameter header was found, start accepting multi paragraph text. -# 3 - Finished (the */ was found) -# 4 - Error - Comment without header was found. Spit a warning as it's not -# proper kernel-doc and ignore the rest. -my $split_doc_state; +# Inline documentation state +use constant { + STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE) + STATE_INLINE_NAME => 1, # looking for member name (@foo:) + STATE_INLINE_TEXT => 2, # looking for member documentation + STATE_INLINE_END => 3, # done + STATE_INLINE_ERROR => 4, # error - Comment without header was found. + # Spit a warning as it's not + # proper kernel-doc and ignore the rest. +}; +my $inline_doc_state; #declaration types: can be # 'function', 'struct', 'union', 'enum', 'typedef' my $decl_type; -my $doc_special = "\@\%\$\&"; - my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start. my $doc_end = '\*/'; my $doc_com = '\s*\*\s*'; my $doc_com_body = '\s*\* ?'; my $doc_decl = $doc_com . '(\w+)'; -my $doc_sect = $doc_com . '([' . $doc_special . ']?[\w\s]+):(.*)'; +# @params and a strictly limited set of supported section names +my $doc_sect = $doc_com . + '\s*(\@\w+|description|context|returns?|notes?|examples?)\s*:(.*)'; my $doc_content = $doc_com_body . '(.*)'; my $doc_block = $doc_com . 'DOC:\s*(.*)?'; -my $doc_split_start = '^\s*/\*\*\s*$'; -my $doc_split_sect = '\s*\*\s*(@[\w\s]+):(.*)'; -my $doc_split_end = '^\s*\*/\s*$'; +my $doc_inline_start = '^\s*/\*\*\s*$'; +my $doc_inline_sect = '\s*\*\s*(@[\w\s]+):(.*)'; +my $doc_inline_end = '^\s*\*/\s*$'; +my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;'; -my %constants; my %parameterdescs; +my %parameterdesc_start_lines; my @parameterlist; my %sections; my @sectionlist; +my %section_start_lines; my $sectcheck; my $struct_actual; my $contents = ""; +my $new_start_line = 0; + +# the canonical section names. see also $doc_sect above. my $section_default = "Description"; # default section my $section_intro = "Introduction"; my $section = $section_default; @@ -437,19 +475,27 @@ while ($ARGV[0] =~ m/^-(.*)/) { } elsif ($cmd eq "-module") { # not needed for XML, inherits from calling document $modulename = shift @ARGV; } elsif ($cmd eq "-function") { # to only output specific functions - $function_only = 1; + $output_selection = OUTPUT_INCLUDE; $function = shift @ARGV; $function_table{$function} = 1; - } elsif ($cmd eq "-nofunction") { # to only output specific functions - $function_only = 2; + } elsif ($cmd eq "-nofunction") { # output all except specific functions + $output_selection = OUTPUT_EXCLUDE; $function = shift @ARGV; $function_table{$function} = 1; + } elsif ($cmd eq "-export") { # only exported symbols + $output_selection = OUTPUT_EXPORTED; + %function_table = () + } elsif ($cmd eq "-internal") { # only non-exported symbols + $output_selection = OUTPUT_INTERNAL; + %function_table = () } elsif ($cmd eq "-v") { $verbose = 1; } elsif (($cmd eq "-h") || ($cmd eq "--help")) { usage(); } elsif ($cmd eq '-no-doc-sections') { $no_doc_sections = 1; + } elsif ($cmd eq '-enable-lineno') { + $enable_lineno = 1; } elsif ($cmd eq '-show-not-found') { $show_not_found = 1; } @@ -467,6 +513,13 @@ sub get_kernel_version() { return $version; } +# +sub print_lineno { + my $lineno = shift; + if ($enable_lineno && defined($lineno)) { + print "#define LINENO " . $lineno . "\n"; + } +} ## # dumps section contents to arrays/hashes intended for that purpose. # @@ -475,28 +528,32 @@ sub dump_section { my $name = shift; my $contents = join "\n", @_; - if ($name =~ m/$type_constant/) { - $name = $1; -# print STDERR "constant section '$1' = '$contents'\n"; - $constants{$name} = $contents; - } elsif ($name =~ m/$type_param/) { + if ($name =~ m/$type_param/) { # print STDERR "parameter def '$1' = '$contents'\n"; $name = $1; $parameterdescs{$name} = $contents; $sectcheck = $sectcheck . $name . " "; + $parameterdesc_start_lines{$name} = $new_start_line; + $new_start_line = 0; } elsif ($name eq "@\.\.\.") { # print STDERR "parameter def '...' = '$contents'\n"; $name = "..."; $parameterdescs{$name} = $contents; $sectcheck = $sectcheck . $name . " "; + $parameterdesc_start_lines{$name} = $new_start_line; + $new_start_line = 0; } else { # print STDERR "other section '$name' = '$contents'\n"; if (defined($sections{$name}) && ($sections{$name} ne "")) { - print STDERR "${file}:$.: error: duplicate section name '$name'\n"; - ++$errors; + print STDERR "${file}:$.: warning: duplicate section name '$name'\n"; + ++$warnings; + $sections{$name} .= $contents; + } else { + $sections{$name} = $contents; + push @sectionlist, $name; + $section_start_lines{$name} = $new_start_line; + $new_start_line = 0; } - $sections{$name} = $contents; - push @sectionlist, $name; } } @@ -512,15 +569,17 @@ sub dump_doc_section { return; } - if (($function_only == 0) || - ( $function_only == 1 && defined($function_table{$name})) || - ( $function_only == 2 && !defined($function_table{$name}))) + if (($output_selection == OUTPUT_ALL) || + ($output_selection == OUTPUT_INCLUDE && + defined($function_table{$name})) || + ($output_selection == OUTPUT_EXCLUDE && + !defined($function_table{$name}))) { dump_section($file, $name, $contents); output_blockhead({'sectionlist' => \@sectionlist, 'sections' => \%sections, 'module' => $modulename, - 'content-only' => ($function_only != 0), }); + 'content-only' => ($output_selection != OUTPUT_ALL), }); } } @@ -1736,7 +1795,10 @@ sub output_blockhead_rst(%) { my ($parameter, $section); foreach $section (@{$args{'sectionlist'}}) { - print "**$section**\n\n"; + if ($output_selection != OUTPUT_INCLUDE) { + print "**$section**\n\n"; + } + print_lineno($section_start_lines{$section}); output_highlight_rst($args{'sections'}{$section}); print "\n"; } @@ -1753,19 +1815,14 @@ sub output_highlight_rst { die $@ if $@; foreach $line (split "\n", $contents) { - if ($line eq "") { - print $lineprefix, $blankline; - } else { - $line =~ s/\\\\\\/\&/g; - print $lineprefix, $line; - } - print "\n"; + print $lineprefix . $line . "\n"; } } sub output_function_rst(%) { my %args = %{$_[0]}; my ($parameter, $section); + my $oldprefix = $lineprefix; my $start; print ".. c:function:: "; @@ -1790,29 +1847,37 @@ sub output_function_rst(%) { print $type . " " . $parameter; } } - print ")\n\n " . $args{'purpose'} . "\n\n"; + print ")\n\n"; + print_lineno($declaration_start_line); + $lineprefix = " "; + output_highlight_rst($args{'purpose'}); + print "\n"; - print ":Parameters:\n\n"; + print "**Parameters**\n\n"; + $lineprefix = " "; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; #$parameter_name =~ s/\[.*//; $type = $args{'parametertypes'}{$parameter}; if ($type ne "") { - print " ``$type $parameter``\n"; + print "``$type $parameter``\n"; } else { - print " ``$parameter``\n"; + print "``$parameter``\n"; } - if ($args{'parameterdescs'}{$parameter_name} ne $undescribed) { - my $oldprefix = $lineprefix; - $lineprefix = " "; + + print_lineno($parameterdesc_start_lines{$parameter_name}); + + if (defined($args{'parameterdescs'}{$parameter_name}) && + $args{'parameterdescs'}{$parameter_name} ne $undescribed) { output_highlight_rst($args{'parameterdescs'}{$parameter_name}); - $lineprefix = $oldprefix; } else { - print "\n _undescribed_\n"; + print " *undescribed*\n"; } print "\n"; } + + $lineprefix = $oldprefix; output_section_rst(@_); } @@ -1820,10 +1885,11 @@ sub output_section_rst(%) { my %args = %{$_[0]}; my $section; my $oldprefix = $lineprefix; - $lineprefix = " "; + $lineprefix = ""; foreach $section (@{$args{'sectionlist'}}) { - print ":$section:\n\n"; + print "**$section**\n\n"; + print_lineno($section_start_lines{$section}); output_highlight_rst($args{'sections'}{$section}); print "\n"; } @@ -1834,24 +1900,28 @@ sub output_section_rst(%) { sub output_enum_rst(%) { my %args = %{$_[0]}; my ($parameter); + my $oldprefix = $lineprefix; my $count; my $name = "enum " . $args{'enum'}; print "\n\n.. c:type:: " . $name . "\n\n"; - print " " . $args{'purpose'} . "\n\n"; + print_lineno($declaration_start_line); + $lineprefix = " "; + output_highlight_rst($args{'purpose'}); + print "\n"; - print "..\n\n:Constants:\n\n"; - my $oldprefix = $lineprefix; - $lineprefix = " "; + print "**Constants**\n\n"; + $lineprefix = " "; foreach $parameter (@{$args{'parameterlist'}}) { - print " `$parameter`\n"; + print "``$parameter``\n"; if ($args{'parameterdescs'}{$parameter} ne $undescribed) { output_highlight_rst($args{'parameterdescs'}{$parameter}); } else { - print " undescribed\n"; + print " *undescribed*\n"; } print "\n"; } + $lineprefix = $oldprefix; output_section_rst(@_); } @@ -1859,30 +1929,37 @@ sub output_enum_rst(%) { sub output_typedef_rst(%) { my %args = %{$_[0]}; my ($parameter); - my $count; + my $oldprefix = $lineprefix; my $name = "typedef " . $args{'typedef'}; - ### FIXME: should the name below contain "typedef" or not? print "\n\n.. c:type:: " . $name . "\n\n"; - print " " . $args{'purpose'} . "\n\n"; + print_lineno($declaration_start_line); + $lineprefix = " "; + output_highlight_rst($args{'purpose'}); + print "\n"; + $lineprefix = $oldprefix; output_section_rst(@_); } sub output_struct_rst(%) { my %args = %{$_[0]}; my ($parameter); + my $oldprefix = $lineprefix; my $name = $args{'type'} . " " . $args{'struct'}; print "\n\n.. c:type:: " . $name . "\n\n"; - print " " . $args{'purpose'} . "\n\n"; + print_lineno($declaration_start_line); + $lineprefix = " "; + output_highlight_rst($args{'purpose'}); + print "\n"; - print ":Definition:\n\n"; - print " ::\n\n"; + print "**Definition**\n\n"; + print "::\n\n"; print " " . $args{'type'} . " " . $args{'struct'} . " {\n"; foreach $parameter (@{$args{'parameterlist'}}) { if ($parameter =~ /^#/) { - print " " . "$parameter\n"; + print " " . "$parameter\n"; next; } @@ -1903,7 +1980,8 @@ sub output_struct_rst(%) { } print " };\n\n"; - print ":Members:\n\n"; + print "**Members**\n\n"; + $lineprefix = " "; foreach $parameter (@{$args{'parameterlist'}}) { ($parameter =~ /^#/) && next; @@ -1912,14 +1990,14 @@ sub output_struct_rst(%) { ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; $type = $args{'parametertypes'}{$parameter}; - print " `$type $parameter`" . "\n"; - my $oldprefix = $lineprefix; - $lineprefix = " "; + print_lineno($parameterdesc_start_lines{$parameter_name}); + print "``$type $parameter``\n"; output_highlight_rst($args{'parameterdescs'}{$parameter_name}); - $lineprefix = $oldprefix; print "\n"; } print "\n"; + + $lineprefix = $oldprefix; output_section_rst(@_); } @@ -1969,9 +2047,13 @@ sub output_declaration { my $name = shift; my $functype = shift; my $func = "output_${functype}_$output_mode"; - if (($function_only==0) || - ( $function_only == 1 && defined($function_table{$name})) || - ( $function_only == 2 && !($functype eq "function" && defined($function_table{$name})))) + if (($output_selection == OUTPUT_ALL) || + (($output_selection == OUTPUT_INCLUDE || + $output_selection == OUTPUT_EXPORTED) && + defined($function_table{$name})) || + (($output_selection == OUTPUT_EXCLUDE || + $output_selection == OUTPUT_INTERNAL) && + !($functype eq "function" && defined($function_table{$name})))) { &$func(@_); $section_counter++; @@ -2471,7 +2553,6 @@ sub dump_function($$) { sub reset_state { $function = ""; - %constants = (); %parameterdescs = (); %parametertypes = (); @parameterlist = (); @@ -2481,8 +2562,8 @@ sub reset_state { $struct_actual = ""; $prototype = ""; - $state = 0; - $split_doc_state = 0; + $state = STATE_NORMAL; + $inline_doc_state = STATE_INLINE_NA; } sub tracepoint_munge($) { @@ -2545,7 +2626,7 @@ sub syscall_munge() { } } -sub process_state3_function($$) { +sub process_proto_function($$) { my $x = shift; my $file = shift; @@ -2575,7 +2656,7 @@ sub process_state3_function($$) { } } -sub process_state3_type($$) { +sub process_proto_type($$) { my $x = shift; my $file = shift; @@ -2657,6 +2738,7 @@ sub process_file($) { my $in_purpose = 0; my $initial_section_counter = $section_counter; my ($orig_file) = @_; + my $leading_space; if (defined($ENV{'SRCTREE'})) { $file = "$ENV{'SRCTREE'}" . "/" . $orig_file; @@ -2674,6 +2756,17 @@ sub process_file($) { return; } + # two passes for -export and -internal + if ($output_selection == OUTPUT_EXPORTED || + $output_selection == OUTPUT_INTERNAL) { + while (<IN>) { + if (/$export_symbol/o) { + $function_table{$2} = 1; + } + } + seek(IN, 0, 0); + } + $. = 1; $section_counter = 0; @@ -2681,15 +2774,18 @@ sub process_file($) { while (s/\\\s*$//) { $_ .= <IN>; } - if ($state == 0) { + if ($state == STATE_NORMAL) { if (/$doc_start/o) { - $state = 1; # next line is always the function name + $state = STATE_NAME; # next line is always the function name $in_doc_sect = 0; + $declaration_start_line = $. + 1; } - } elsif ($state == 1) { # this line is the function name (always) + } elsif ($state == STATE_NAME) {# this line is the function name (always) if (/$doc_block/o) { - $state = 4; + $state = STATE_DOCBLOCK; $contents = ""; + $new_start_line = $. + 1; + if ( $1 eq "" ) { $section = $section_intro; } else { @@ -2702,7 +2798,12 @@ sub process_file($) { $identifier = $1; } - $state = 2; + $state = STATE_FIELD; + # if there's no @param blocks need to set up default section + # here + $contents = ""; + $section = $section_default; + $new_start_line = $. + 1; if (/-(.*)/) { # strip leading/trailing/multiple spaces $descr= $1; @@ -2740,13 +2841,25 @@ sub process_file($) { print STDERR "${file}:$.: warning: Cannot understand $_ on line $.", " - I thought it was a doc line\n"; ++$warnings; - $state = 0; + $state = STATE_NORMAL; } - } elsif ($state == 2) { # look for head: lines, and include content - if (/$doc_sect/o) { + } elsif ($state == STATE_FIELD) { # look for head: lines, and include content + if (/$doc_sect/i) { # case insensitive for supported section names $newsection = $1; $newcontents = $2; + # map the supported section names to the canonical names + if ($newsection =~ m/^description$/i) { + $newsection = $section_default; + } elsif ($newsection =~ m/^context$/i) { + $newsection = $section_context; + } elsif ($newsection =~ m/^returns?$/i) { + $newsection = $section_return; + } elsif ($newsection =~ m/^\@return$/) { + # special: @return is a section, not a param description + $newsection = $section_return; + } + if (($contents ne "") && ($contents ne "\n")) { if (!$in_doc_sect && $verbose) { print STDERR "${file}:$.: warning: contents before sections\n"; @@ -2759,14 +2872,16 @@ sub process_file($) { $in_doc_sect = 1; $in_purpose = 0; $contents = $newcontents; + $new_start_line = $.; + while ((substr($contents, 0, 1) eq " ") || + substr($contents, 0, 1) eq "\t") { + $contents = substr($contents, 1); + } if ($contents ne "") { - while ((substr($contents, 0, 1) eq " ") || - substr($contents, 0, 1) eq "\t") { - $contents = substr($contents, 1); - } $contents .= "\n"; } $section = $newsection; + $leading_space = undef; } elsif (/$doc_end/) { if (($contents ne "") && ($contents ne "\n")) { dump_section($file, $section, xml_escape($contents)); @@ -2780,7 +2895,7 @@ sub process_file($) { } $prototype = ""; - $state = 3; + $state = STATE_PROTO; $brcount = 0; # print STDERR "end of doc comment, looking for prototype\n"; } elsif (/$doc_content/) { @@ -2791,6 +2906,7 @@ sub process_file($) { dump_section($file, $section, xml_escape($contents)); $section = $section_default; $contents = ""; + $new_start_line = $.; } else { $contents .= "\n"; } @@ -2801,87 +2917,86 @@ sub process_file($) { $declaration_purpose .= " " . xml_escape($1); $declaration_purpose =~ s/\s+/ /g; } else { - $contents .= $1 . "\n"; + my $cont = $1; + if ($section =~ m/^@/ || $section eq $section_context) { + if (!defined $leading_space) { + if ($cont =~ m/^(\s+)/) { + $leading_space = $1; + } else { + $leading_space = ""; + } + } + + $cont =~ s/^$leading_space//; + } + $contents .= $cont . "\n"; } } else { # i dont know - bad line? ignore. print STDERR "${file}:$.: warning: bad line: $_"; ++$warnings; } - } elsif ($state == 5) { # scanning for split parameters + } elsif ($state == STATE_INLINE) { # scanning for inline parameters # First line (state 1) needs to be a @parameter - if ($split_doc_state == 1 && /$doc_split_sect/o) { + if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { $section = $1; $contents = $2; + $new_start_line = $.; if ($contents ne "") { while ((substr($contents, 0, 1) eq " ") || substr($contents, 0, 1) eq "\t") { $contents = substr($contents, 1); } - $contents .= "\n"; + $contents .= "\n"; } - $split_doc_state = 2; + $inline_doc_state = STATE_INLINE_TEXT; # Documentation block end */ - } elsif (/$doc_split_end/) { + } elsif (/$doc_inline_end/) { if (($contents ne "") && ($contents ne "\n")) { dump_section($file, $section, xml_escape($contents)); $section = $section_default; $contents = ""; } - $state = 3; - $split_doc_state = 0; + $state = STATE_PROTO; + $inline_doc_state = STATE_INLINE_NA; # Regular text } elsif (/$doc_content/) { - if ($split_doc_state == 2) { + if ($inline_doc_state == STATE_INLINE_TEXT) { $contents .= $1 . "\n"; - } elsif ($split_doc_state == 1) { - $split_doc_state = 4; + # nuke leading blank lines + if ($contents =~ /^\s*$/) { + $contents = ""; + } + } elsif ($inline_doc_state == STATE_INLINE_NAME) { + $inline_doc_state = STATE_INLINE_ERROR; print STDERR "Warning(${file}:$.): "; print STDERR "Incorrect use of kernel-doc format: $_"; ++$warnings; } } - } elsif ($state == 3) { # scanning for function '{' (end of prototype) - if (/$doc_split_start/) { - $state = 5; - $split_doc_state = 1; + } elsif ($state == STATE_PROTO) { # scanning for function '{' (end of prototype) + if (/$doc_inline_start/) { + $state = STATE_INLINE; + $inline_doc_state = STATE_INLINE_NAME; } elsif ($decl_type eq 'function') { - process_state3_function($_, $file); + process_proto_function($_, $file); } else { - process_state3_type($_, $file); + process_proto_type($_, $file); } - } elsif ($state == 4) { - # Documentation block - if (/$doc_block/) { - dump_doc_section($file, $section, xml_escape($contents)); - $contents = ""; - $function = ""; - %constants = (); - %parameterdescs = (); - %parametertypes = (); - @parameterlist = (); - %sections = (); - @sectionlist = (); - $prototype = ""; - if ( $1 eq "" ) { - $section = $section_intro; - } else { - $section = $1; - } - } - elsif (/$doc_end/) + } elsif ($state == STATE_DOCBLOCK) { + if (/$doc_end/) { dump_doc_section($file, $section, xml_escape($contents)); + $section = $section_default; $contents = ""; $function = ""; - %constants = (); %parameterdescs = (); %parametertypes = (); @parameterlist = (); %sections = (); @sectionlist = (); $prototype = ""; - $state = 0; + $state = STATE_NORMAL; } elsif (/$doc_content/) { @@ -2898,7 +3013,7 @@ sub process_file($) { } if ($initial_section_counter == $section_counter) { print STDERR "${file}:1: warning: no structured comments found\n"; - if (($function_only == 1) && ($show_not_found == 1)) { + if (($output_selection == OUTPUT_INCLUDE) && ($show_not_found == 1)) { print STDERR " Was looking for '$_'.\n" for keys %function_table; } if ($output_mode eq "xml") { |