Merge tag 'topic/drm-misc-2016-06-15' of git://anongit.freedesktop.org/drm-intel into drm-next
- best_encoder cleanup from Boris. - drm_simple_display_pipe helpers from Noralf. Looks really neat imo, and there's 2-3 in-flight drivers which look like they could/should use it. Anyway, with this we have now helpers and everything in place to write drivers for simple hw with fewer complexity in the driver than what fbdev would need. That was the last complaint I've heard from embedded folks after we made atomic happen. Mission accomplished! - nonblocking commit helpers for atomic, plus a bunch of driver patches for that. - Prep patch from Laurent for cleaned up pixel format functions. - More of Gustavo's cleanup for drm vblank functions. - and a few oddball things in between Plus the merge of docs-next to prep the docbook->sphinx conversion as discussed. Jon cc'ed as fyi. * tag 'topic/drm-misc-2016-06-15' of git://anongit.freedesktop.org/drm-intel: (108 commits) drm/atomic-helpers: Clear up cleanup_done a bit drm/atomic-helpers: Stall on the right commit drm/vmwgfx: use *_32_bits() macros drm/virtio: Don't reinvent a flipping wheel drm/i915: Fix missing unlock on error in i915_ppgtt_info() drm/gma500: use drm_crtc_vblank_{on,off}() drm/radeon: use crtc directly in drm_crtc_vblank_put() drm/amdgpu: use crtc directly in drm_crtc_vblank_put() drm/radeon: use drm_crtc_vblank_{on,off}() drm/amdgpu: use drm_crtc_vblank_{on,off}() drm: make drm_vblank_{get,put}() static drm: remove legacy drm_arm_vblank_event() drm: remove legacy drm_send_vblank_event() drm/nouveau: replace legacy vblank helpers drm/prime: fix error path deadlock fail drm/dsi: Add uevent callback drm: fb: cma: fix memory leak drm: i915: Rely on the default ->best_encoder() behavior where appropriate drm: Add helper for simple display pipeline drm/bridge: dw-hdmi: Use drm_atomic_helper_best_encoder() ...
This commit is contained in:
Коммит
a0877f5203
|
@ -0,0 +1 @@
|
|||
output
|
|
@ -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
|
||||
|
|
|
@ -1017,6 +1017,11 @@ int max_width, max_height;</synopsis>
|
|||
<function>drm_framebuffer_unregister_private</function>.
|
||||
</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>
|
||||
|
@ -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 -->
|
||||
|
|
|
@ -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
|
|
@ -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 = '..'
|
|
@ -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
|
||||
|
|
|
@ -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`
|
||||
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>%
|
|
@ -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)
|
|
@ -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:]]*$//
|
|
@ -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
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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();
|
||||
do_that();
|
||||
} else {
|
||||
otherwise();
|
||||
}
|
||||
if (condition)
|
||||
do_this();
|
||||
else
|
||||
do_that();
|
||||
|
||||
这并不适用于只有一个条件分支是单语句的情况;这时所有分支都要使用大括号:
|
||||
|
||||
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);
|
||||
int fun(int a)
|
||||
{
|
||||
int result = 0;
|
||||
char *buffer;
|
||||
|
||||
if (buffer == NULL)
|
||||
return -ENOMEM;
|
||||
buffer = kmalloc(SIZE, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (condition1) {
|
||||
while (loop1) {
|
||||
...
|
||||
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)
|
||||
这没什么,我们都是这样。可能你的使用了很长时间 Unix 的朋友已经告诉你 “GNU emacs” 能
|
||||
自动帮你格式化 C 源代码,而且你也注意到了,确实是这样,不过它所使用的默认值和我们
|
||||
想要的相去甚远(实际上,甚至比随机打的还要差——无数个猴子在 GNU emacs 里打字永远不
|
||||
会创造出一个好程序)(译注:请参考 Infinite Monkey Theorem)
|
||||
|
||||
所以你要么放弃GNU emacs,要么改变它让它使用更合理的设定。要采用后一个方案,你可
|
||||
以把下面这段粘贴到你的.emacs文件里。
|
||||
所以你要么放弃 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))
|
||||
(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)))
|
||||
|
||||
这样就定义了M-x linux-c-mode命令。当你hack一个模块的时候,如果你把字符串
|
||||
-*- linux-c -*-放在头两行的某个位置,这个模式将会被自动调用。如果你希望在你修改
|
||||
/usr/src/linux里的文件时魔术般自动打开linux-c-mode的话,你也可能需要添加
|
||||
(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))))))
|
||||
|
||||
(setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.[ch]$" . linux-c-mode)
|
||||
auto-mode-alist))
|
||||
(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文件里。
|
||||
这会让 emacs 在 ~/src/linux-trees 目录下的 C 源文件获得更好的内核代码风格。
|
||||
|
||||
不过就算你尝试让emacs正确的格式化代码失败了,也并不意味着你失去了一切:还可以用“
|
||||
indent”。
|
||||
不过就算你尝试让 emacs 正确的格式化代码失败了,也并不意味着你失去了一切:还可以用
|
||||
“indent”。
|
||||
|
||||
不过,GNU indent也有和GNU emacs一样有问题的设定,所以你需要给它一些命令选项。不
|
||||
过,这还不算太糟糕,因为就算是GNU indent的作者也认同K&R的权威性(GNU的人并不是坏
|
||||
人,他们只是在这个问题上被严重的误导了),所以你只要给indent指定选项“-kr -i8”
|
||||
(代表“K&R,8个字符缩进”),或者使用“scripts/Lindent”,这样就可以以最时髦的方式
|
||||
不过,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)
|
||||
|
||||
cpp手册对宏的讲解很详细。Gcc internals手册也详细讲解了RTL(译注:register
|
||||
5) 在宏里定义类似函数的本地变量时命名冲突:
|
||||
|
||||
#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日。
|
||||
|
|
5
Makefile
5
Makefile
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* RETURNS
|
||||
* 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:
|
||||
* 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) {
|
||||
|
|
|
@ -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)
|
||||
|
@ -5502,264 +5474,6 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
|
|||
return dev->driver->dumb_destroy(file_priv, dev, args->handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -1000,34 +1000,6 @@ static void send_vblank_event(struct drm_device *dev,
|
|||
e->event.sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
}
|
||||
|
||||
static int fsl_dcu_drm_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -26,18 +26,6 @@
|
|||
#include "rcar_du_lvdsenc.h"
|
||||
#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
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -349,20 +349,11 @@ static int rockchip_dp_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#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);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops rockchip_dp_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(rockchip_dp_suspend, rockchip_dp_resume)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.suspend = analogix_dp_suspend,
|
||||
.resume_early = analogix_dp_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_dp_dt_ids[] = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
void rockchip_drm_fb_suspend(struct drm_device *drm)
|
||||
{
|
||||
struct rockchip_drm_private *priv = drm->dev_private;
|
||||
|
||||
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;
|
||||
|
||||
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 drm_connector *connector;
|
||||
struct rockchip_drm_private *priv = drm->dev_private;
|
||||
|
||||
if (!drm)
|
||||
return 0;
|
||||
drm_kms_helper_poll_disable(drm);
|
||||
rockchip_drm_fb_suspend(drm);
|
||||
|
||||
drm_modeset_lock_all(drm);
|
||||
list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
|
||||
int old_dpms = connector->dpms;
|
||||
|
||||
if (connector->funcs->dpms)
|
||||
connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
|
||||
|
||||
/* 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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче