Commit 9253d059 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'topic/drm-misc-2016-06-22-updated' of...

Merge tag 'topic/drm-misc-2016-06-22-updated' of git://anongit.freedesktop.org/drm-intel into drm-next

Again a pile of things all over
- Conversion to rst from docbook from Jani. Looks real pretty, and the
  source is now actually readable (compared to horrible, horrible docbook
  xml)! https://01.org/linuxgraphics/gfx-docs/drm/
- device register/unregister rework from Chris, with follow-up work from
  Benjamin. Allows more drivers to demidlayer load/unload and others to
  remove a bit of boilerplate.
- master/auth related cleanup, with docs
- some dma-buf polish, merged by Sumit
- small stuff all over (like build fixes from Arnd)

Group maintainership seems to slowly take off, with both Thierry and Sumit
pushing a few things. No hiccups thus far.

* tag 'topic/drm-misc-2016-06-22-updated' of git://anongit.freedesktop.org/drm-intel: (68 commits)
  drm/vc4: Remove unused connector
  drm/fb-helper: Reduce READ_ONCE(master) to lockless_dereference
  drm/sun4i: Remove open-coded drm_connector_register_all()
  drm/vc4: Remove open-coded drm_connector_register_all()
  drm/atmel-hlcdc: Remove redundant call to drm_connector_unregister_all()
  drm: document drm_auth.c
  drm: Clear up master tracking booleans
  drm: Extract drm_is_current_master
  drm: Refactor drop/set master code a bit
  drm: Lobotomize set_busid nonsense for !pci drivers
  drm: Nuke SET_UNIQUE ioctl
  drm: Don't call drm_dev_set_unique from platform drivers
  drm/vgem: Stop calling drm_drv_set_unique
  drm: Use dev->name as fallback for dev->unique
  drm: Clean up drm_crtc.h
  drm: Move master pointer from drm_minor to drm_device
  drm: sti: rework init sequence
  drm: sti: use late_register and early_unregister callbacks
  drm/amdkfd: Clean up inline handling
  drm: Add callbacks for late registering
  ...
parents 4b01ec97 f510f34c
...@@ -14,7 +14,7 @@ DOCBOOKS := z8530book.xml device-drivers.xml \ ...@@ -14,7 +14,7 @@ DOCBOOKS := z8530book.xml device-drivers.xml \
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \ genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
80211.xml debugobjects.xml sh.xml regulator.xml \ 80211.xml debugobjects.xml sh.xml regulator.xml \
alsa-driver-api.xml writing-an-alsa-driver.xml \ alsa-driver-api.xml writing-an-alsa-driver.xml \
tracepoint.xml gpu.xml media_api.xml w1.xml \ tracepoint.xml media_api.xml w1.xml \
writing_musb_glue_layer.xml crypto-API.xml iio.xml writing_musb_glue_layer.xml crypto-API.xml iio.xml
include Documentation/DocBook/media/Makefile include Documentation/DocBook/media/Makefile
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
=============================
Mode Setting Helper Functions
=============================
The plane, CRTC, encoder and connector functions provided by the drivers
implement the DRM API. They're called by the DRM core and ioctl handlers
to handle device state changes and configuration request. As
implementing those functions often requires logic not specific to
drivers, mid-layer helper functions are available to avoid duplicating
boilerplate code.
The DRM core contains one mid-layer implementation. The mid-layer
provides implementations of several plane, CRTC, encoder and connector
functions (called from the top of the mid-layer) that pre-process
requests and call lower-level functions provided by the driver (at the
bottom of the mid-layer). For instance, the
:c:func:`drm_crtc_helper_set_config()` function can be used to
fill the :c:type:`struct drm_crtc_funcs <drm_crtc_funcs>`
set_config field. When called, it will split the set_config operation
in smaller, simpler operations and call the driver to handle them.
To use the mid-layer, drivers call
:c:func:`drm_crtc_helper_add()`,
:c:func:`drm_encoder_helper_add()` and
:c:func:`drm_connector_helper_add()` functions to install their
mid-layer bottom operations handlers, and fill the :c:type:`struct
drm_crtc_funcs <drm_crtc_funcs>`, :c:type:`struct
drm_encoder_funcs <drm_encoder_funcs>` and :c:type:`struct
drm_connector_funcs <drm_connector_funcs>` structures with
pointers to the mid-layer top API functions. Installing the mid-layer
bottom operation handlers is best done right after registering the
corresponding KMS object.
The mid-layer is not split between CRTC, encoder and connector
operations. To use it, a driver must provide bottom functions for all of
the three KMS entities.
Atomic Modeset Helper Functions Reference
=========================================
Overview
--------
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
:doc: overview
Implementing Asynchronous Atomic Commit
---------------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
:doc: implementing nonblocking commit
Atomic State Reset and Initialization
-------------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
:doc: atomic state reset and initialization
.. kernel-doc:: include/drm/drm_atomic_helper.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
:export:
Modeset Helper Reference for Common Vtables
===========================================
.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
:internal:
.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
:doc: overview
Legacy CRTC/Modeset Helper Functions Reference
==============================================
.. kernel-doc:: drivers/gpu/drm/drm_crtc_helper.c
:export:
.. kernel-doc:: drivers/gpu/drm/drm_crtc_helper.c
:doc: overview
Output Probing Helper Functions Reference
=========================================
.. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c
:doc: output probing helper overview
.. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c
:export:
fbdev Helper Functions Reference
================================
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
:doc: fbdev helpers
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
:export:
.. kernel-doc:: include/drm/drm_fb_helper.h
:internal:
Framebuffer CMA Helper Functions Reference
==========================================
.. kernel-doc:: drivers/gpu/drm/drm_fb_cma_helper.c
:doc: framebuffer cma helper functions
.. kernel-doc:: drivers/gpu/drm/drm_fb_cma_helper.c
:export:
Display Port Helper Functions Reference
=======================================
.. kernel-doc:: drivers/gpu/drm/drm_dp_helper.c
:doc: dp helpers
.. kernel-doc:: include/drm/drm_dp_helper.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_dp_helper.c
:export:
Display Port Dual Mode Adaptor Helper Functions Reference
=========================================================
.. kernel-doc:: drivers/gpu/drm/drm_dp_dual_mode_helper.c
:doc: dp dual mode helpers
.. kernel-doc:: include/drm/drm_dp_dual_mode_helper.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_dp_dual_mode_helper.c
:export:
Display Port MST Helper Functions Reference
===========================================
.. kernel-doc:: drivers/gpu/drm/drm_dp_mst_topology.c
:doc: dp mst helper
.. kernel-doc:: include/drm/drm_dp_mst_helper.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_dp_mst_topology.c
:export:
MIPI DSI Helper Functions Reference
===================================
.. kernel-doc:: drivers/gpu/drm/drm_mipi_dsi.c
:doc: dsi helpers
.. kernel-doc:: include/drm/drm_mipi_dsi.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_mipi_dsi.c
:export:
EDID Helper Functions Reference
===============================
.. kernel-doc:: drivers/gpu/drm/drm_edid.c
:export:
Rectangle Utilities Reference
=============================
.. kernel-doc:: include/drm/drm_rect.h
:doc: rect utils
.. kernel-doc:: include/drm/drm_rect.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_rect.c
:export:
Flip-work Helper Reference
==========================
.. kernel-doc:: include/drm/drm_flip_work.h
:doc: flip utils
.. kernel-doc:: include/drm/drm_flip_work.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_flip_work.c
:export:
HDMI Infoframes Helper Reference
================================
Strictly speaking this is not a DRM helper library but generally useable
by any driver interfacing with HDMI outputs like v4l or alsa drivers.
But it nicely fits into the overall topic of mode setting helper
libraries and hence is also included here.
.. kernel-doc:: include/linux/hdmi.h
:internal:
.. kernel-doc:: drivers/video/hdmi.c
:export:
Plane Helper Reference
======================
.. kernel-doc:: drivers/gpu/drm/drm_plane_helper.c
:export:
.. kernel-doc:: drivers/gpu/drm/drm_plane_helper.c
:doc: overview
Tile group
----------
.. kernel-doc:: drivers/gpu/drm/drm_crtc.c
:doc: Tile group
Bridges
=======
Overview
--------
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: overview
Default bridge callback sequence
--------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: bridge callbacks
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:export:
Panel Helper Reference
======================
.. kernel-doc:: include/drm/drm_panel.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
:export:
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
:doc: drm panel
Simple KMS Helper Reference
===========================
.. kernel-doc:: include/drm/drm_simple_kms_helper.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
:export:
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
:doc: overview
This diff is collapsed.
This diff is collapsed.
===================
Userland interfaces
===================
The DRM core exports several interfaces to applications, generally
intended to be used through corresponding libdrm wrapper functions. In
addition, drivers export device-specific interfaces for use by userspace
drivers & device-aware applications through ioctls and sysfs files.
External interfaces include: memory mapping, context management, DMA
operations, AGP management, vblank control, fence management, memory
management, and output management.
Cover generic ioctls and sysfs layout here. We only need high-level
info, since man pages should cover the rest.
libdrm Device Lookup
====================
.. kernel-doc:: drivers/gpu/drm/drm_ioctl.c
:doc: getunique and setversion story
Primary Nodes, DRM Master and Authentication
============================================
.. kernel-doc:: drivers/gpu/drm/drm_auth.c
:doc: master and authentication
.. kernel-doc:: drivers/gpu/drm/drm_auth.c
:export:
.. kernel-doc:: include/drm/drm_auth.h
:internal:
Render nodes
============
DRM core provides multiple character-devices for user-space to use.
Depending on which device is opened, user-space can perform a different
set of operations (mainly ioctls). The primary node is always created
and called card<num>. Additionally, a currently unused control node,
called controlD<num> is also created. The primary node provides all
legacy operations and historically was the only interface used by
userspace. With KMS, the control node was introduced. However, the
planned KMS control interface has never been written and so the control
node stays unused to date.
With the increased use of offscreen renderers and GPGPU applications,
clients no longer require running compositors or graphics servers to
make use of a GPU. But the DRM API required unprivileged clients to
authenticate to a DRM-Master prior to getting GPU access. To avoid this
step and to grant clients GPU access without authenticating, render
nodes were introduced. Render nodes solely serve render clients, that
is, no modesetting or privileged ioctls can be issued on render nodes.
Only non-global rendering commands are allowed. If a driver supports
render nodes, it must advertise it via the DRIVER_RENDER DRM driver
capability. If not supported, the primary node must be used for render
clients together with the legacy drmAuth authentication procedure.
If a driver advertises render node support, DRM core will create a
separate render node called renderD<num>. There will be one render node
per device. No ioctls except PRIME-related ioctls will be allowed on
this node. Especially GEM_OPEN will be explicitly prohibited. Render
nodes are designed to avoid the buffer-leaks, which occur if clients
guess the flink names or mmap offsets on the legacy interface.
Additionally to this basic interface, drivers must mark their
driver-dependent render-only ioctls as DRM_RENDER_ALLOW so render
clients can use them. Driver authors must be careful not to allow any
privileged ioctls on render nodes.
With render nodes, user-space can now control access to the render node
via basic file-system access-modes. A running graphics server which
authenticates clients on the privileged primary/legacy node is no longer
required. Instead, a client can open the render node and is immediately
granted GPU access. Communication between clients (or servers) is done
via PRIME. FLINK from render node to legacy node is not supported. New
clients must not use the insecure FLINK interface.
Besides dropping all modeset/global ioctls, render nodes also drop the
DRM-Master concept. There is no reason to associate render clients with
a DRM-Master as they are independent of any graphics server. Besides,
they must work without any running master, anyway. Drivers must be able
to run without a master object if they support render nodes. If, on the
other hand, a driver requires shared state between clients which is
visible to user-space and accessible beyond open-file boundaries, they
cannot support render nodes.
VBlank event handling
=====================
The DRM core exposes two vertical blank related ioctls:
DRM_IOCTL_WAIT_VBLANK
This takes a struct drm_wait_vblank structure as its argument, and
it is used to block or request a signal when a specified vblank
event occurs.
DRM_IOCTL_MODESET_CTL
This was only used for user-mode-settind drivers around modesetting
changes to allow the kernel to update the vblank interrupt after
mode setting, since on many devices the vertical blank counter is
reset to 0 at some point during modeset. Modern drivers should not
call this any more since with kernel mode setting it is a no-op.
This second part of the GPU Driver Developer's Guide documents driver
code, implementation details and also all the driver-specific userspace
interfaces. Especially since all hardware-acceleration interfaces to
userspace are driver specific for efficiency and other reasons these
interfaces can be rather substantial. Hence every driver has its own
chapter.
===========================
drm/i915 Intel GFX Driver
===========================
The drm/i915 driver supports all (with the exception of some very early
models) integrated GFX chipsets with both Intel display and rendering
blocks. This excludes a set of SoC platforms with an SGX rendering unit,
those have basic support through the gma500 drm driver.
Core Driver Infrastructure
==========================
This section covers core driver infrastructure used by both the display
and the GEM parts of the driver.
Runtime Power Management
------------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_runtime_pm.c
:doc: runtime pm
.. kernel-doc:: drivers/gpu/drm/i915/intel_runtime_pm.c
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/intel_uncore.c
:internal:
Interrupt Handling
------------------
.. kernel-doc:: drivers/gpu/drm/i915/i915_irq.c
:doc: interrupt handling
.. kernel-doc:: drivers/gpu/drm/i915/i915_irq.c
:functions: intel_irq_init intel_irq_init_hw intel_hpd_init
.. kernel-doc:: drivers/gpu/drm/i915/i915_irq.c
:functions: intel_runtime_pm_disable_interrupts
.. kernel-doc:: drivers/gpu/drm/i915/i915_irq.c
:functions: intel_runtime_pm_enable_interrupts
Intel GVT-g Guest Support(vGPU)
-------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/i915_vgpu.c
:doc: Intel GVT-g guest support
.. kernel-doc:: drivers/gpu/drm/i915/i915_vgpu.c
:internal:
Display Hardware Handling
=========================
This section covers everything related to the display hardware including
the mode setting infrastructure, plane, sprite and cursor handling and
display, output probing and related topics.
Mode Setting Infrastructure
---------------------------
The i915 driver is thus far the only DRM driver which doesn't use the
common DRM helper code to implement mode setting sequences. Thus it has
its own tailor-made infrastructure for executing a display configuration
change.
Frontbuffer Tracking
--------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_frontbuffer.c
:doc: frontbuffer tracking
.. kernel-doc:: drivers/gpu/drm/i915/intel_frontbuffer.c
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem.c
:functions: i915_gem_track_fb
Display FIFO Underrun Reporting
-------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_fifo_underrun.c
:doc: fifo underrun handling
.. kernel-doc:: drivers/gpu/drm/i915/intel_fifo_underrun.c
:internal:
Plane Configuration
-------------------
This section covers plane configuration and composition with the primary
plane, sprites, cursors and overlays. This includes the infrastructure
to do atomic vsync'ed updates of all this state and also tightly coupled
topics like watermark setup and computation, framebuffer compression and
panel self refresh.
Atomic Plane Helpers
--------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_atomic_plane.c
:doc: atomic plane helpers
.. kernel-doc:: drivers/gpu/drm/i915/intel_atomic_plane.c
:internal:
Output Probing
--------------
This section covers output probing and related infrastructure like the
hotplug interrupt storm detection and mitigation code. Note that the
i915 driver still uses most of the common DRM helper code for output
probing, so those sections fully apply.
Hotplug
-------
.. kernel-doc:: drivers/gpu/drm/i915/intel_hotplug.c
:doc: Hotplug
.. kernel-doc:: drivers/gpu/drm/i915/intel_hotplug.c
:internal:
High Definition Audio
---------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_audio.c
:doc: High Definition Audio over HDMI and Display Port
.. kernel-doc:: drivers/gpu/drm/i915/intel_audio.c
:internal:
.. kernel-doc:: include/drm/i915_component.h
:internal:
Panel Self Refresh PSR (PSR/SRD)
--------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_psr.c
:doc: Panel Self Refresh (PSR/SRD)
.. kernel-doc:: drivers/gpu/drm/i915/intel_psr.c
:internal:
Frame Buffer Compression (FBC)
------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_fbc.c
:doc: Frame Buffer Compression (FBC)
.. kernel-doc:: drivers/gpu/drm/i915/intel_fbc.c
:internal:
Display Refresh Rate Switching (DRRS)
-------------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_dp.c
:doc: Display Refresh Rate Switching (DRRS)
.. kernel-doc:: drivers/gpu/drm/i915/intel_dp.c
:functions: intel_dp_set_drrs_state
.. kernel-doc:: drivers/gpu/drm/i915/intel_dp.c
:functions: intel_edp_drrs_enable
.. kernel-doc:: drivers/gpu/drm/i915/intel_dp.c
:functions: intel_edp_drrs_disable
.. kernel-doc:: drivers/gpu/drm/i915/intel_dp.c
:functions: intel_edp_drrs_invalidate
.. kernel-doc:: drivers/gpu/drm/i915/intel_dp.c
:functions: intel_edp_drrs_flush
.. kernel-doc:: drivers/gpu/drm/i915/intel_dp.c
:functions: intel_dp_drrs_init
DPIO
----
.. kernel-doc:: drivers/gpu/drm/i915/i915_reg.h
:doc: DPIO
CSR firmware support for DMC
----------------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_csr.c
:doc: csr support for dmc
.. kernel-doc:: drivers/gpu/drm/i915/intel_csr.c
:internal:
Video BIOS Table (VBT)
----------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_bios.c
:doc: Video BIOS Table (VBT)
.. kernel-doc:: drivers/gpu/drm/i915/intel_bios.c
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/intel_vbt_defs.h
:internal:
Memory Management and Command Submission
========================================
This sections covers all things related to the GEM implementation in the
i915 driver.
Batchbuffer Parsing
-------------------
.. kernel-doc:: drivers/gpu/drm/i915/i915_cmd_parser.c
:doc: batch buffer command parser
.. kernel-doc:: drivers/gpu/drm/i915/i915_cmd_parser.c
:internal:
Batchbuffer Pools
-----------------
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_batch_pool.c
:doc: batch pool
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_batch_pool.c
:internal:
Logical Rings, Logical Ring Contexts and Execlists
--------------------------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_lrc.c
:doc: Logical Rings, Logical Ring Contexts and Execlists
.. kernel-doc:: drivers/gpu/drm/i915/intel_lrc.c
:internal:
Global GTT views
----------------
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_gtt.c
:doc: Global GTT views
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_gtt.c
:internal:
GTT Fences and Swizzling
------------------------
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_fence.c
:internal:
Global GTT Fence Handling
~~~~~~~~~~~~~~~~~~~~~~~~~
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_fence.c
:doc: fence register handling
Hardware Tiling and Swizzling Details
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_fence.c
:doc: tiling swizzling details
Object Tiling IOCTLs
--------------------
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_tiling.c
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_tiling.c
:doc: buffer object tiling
Buffer Object Eviction
----------------------
This section documents the interface functions for evicting buffer
objects to make space available in the virtual gpu address spaces. Note
that this is mostly orthogonal to shrinking buffer objects caches, which
has the goal to make main memory (shared with the gpu through the
unified memory architecture) available.
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_evict.c
:internal:
Buffer Object Memory Shrinking
------------------------------
This section documents the interface function for shrinking memory usage
of buffer object caches. Shrinking is used to make main memory
available. Note that this is mostly orthogonal to evicting buffer
objects, which has the goal to make space in gpu virtual address spaces.
.. kernel-doc:: drivers/gpu/drm/i915/i915_gem_shrinker.c
:internal:
GuC
===
GuC-specific firmware loader
----------------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_guc_loader.c
:doc: GuC-specific firmware loader
.. kernel-doc:: drivers/gpu/drm/i915/intel_guc_loader.c
:internal:
GuC-based command submission
----------------------------
.. kernel-doc:: drivers/gpu/drm/i915/i915_guc_submission.c
:doc: GuC-based command submission
.. kernel-doc:: drivers/gpu/drm/i915/i915_guc_submission.c
:internal:
GuC Firmware Layout
-------------------
.. kernel-doc:: drivers/gpu/drm/i915/intel_guc_fwif.h
:doc: GuC Firmware Layout
Tracing
=======
This sections covers all things related to the tracepoints implemented
in the i915 driver.
i915_ppgtt_create and i915_ppgtt_release
----------------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/i915_trace.h
:doc: i915_ppgtt_create and i915_ppgtt_release tracepoints
i915_context_create and i915_context_free
-----------------------------------------
.. kernel-doc:: drivers/gpu/drm/i915/i915_trace.h
:doc: i915_context_create and i915_context_free tracepoints
switch_mm
---------
.. kernel-doc:: drivers/gpu/drm/i915/i915_trace.h
:doc: switch_mm tracepoint
.. WARNING: DOCPROC directive not supported: !Cdrivers/gpu/drm/i915/i915_irq.c
==================================
Linux GPU Driver Developer's Guide
==================================
.. toctree::
introduction
drm-internals
drm-mm
drm-kms
drm-kms-helpers
drm-uapi
i915
vga-switcheroo
============
Introduction
============
The Linux DRM layer contains code intended to support the needs of
complex graphics devices, usually containing programmable pipelines well
suited to 3D graphics acceleration. Graphics drivers in the kernel may
make use of DRM functions to make tasks like memory management,
interrupt handling and DMA easier, and provide a uniform interface to
applications.
A note on versions: this guide covers features found in the DRM tree,
including the TTM memory manager, output configuration and mode setting,
and the new vblank internals, in addition to all the regular features
found in current kernels.
[Insert diagram of typical DRM stack here]
Style Guidelines
================
For consistency this documentation uses American English. Abbreviations
are written as all-uppercase, for example: DRM, KMS, IOCTL, CRTC, and so
on. To aid in reading, documentations make full use of the markup
characters kerneldoc provides: @parameter for function parameters,
@member for structure members, &structure to reference structures and
function() for functions. These all get automatically hyperlinked if
kerneldoc for the referenced objects exists. When referencing entries in
function vtables please use ->vfunc(). Note that kerneldoc does not
support referencing struct members directly, so please add a reference
to the vtable struct somewhere in the same paragraph or at least
section.
Except in special situations (to separate locked from unlocked variants)
locking requirements for functions aren't documented in the kerneldoc.
Instead locking should be check at runtime using e.g.
``WARN_ON(!mutex_is_locked(...));``. Since it's much easier to ignore
documentation than runtime noise this provides more value. And on top of
that runtime checks do need to be updated when the locking rules change,
increasing the chances that they're correct. Within the documentation
the locking rules should be explained in the relevant structures: Either
in the comment for the lock explaining what it protects, or data fields
need a note about which lock protects them, or both.
Functions which have a non-\ ``void`` return value should have a section
called "Returns" explaining the expected return values in different
cases and their meanings. Currently there's no consensus whether that
section name should be all upper-case or not, and whether it should end
in a colon or not. Go with the file-local style. Other common section
names are "Notes" with information for dangerous or tricky corner cases,
and "FIXME" where the interface could be cleaned up.
This diff is collapsed.
==============
VGA Switcheroo
==============
.. kernel-doc:: drivers/gpu/vga/vga_switcheroo.c
:doc: Overview
Modes of Use
============
Manual switching and manual power control
-----------------------------------------
.. kernel-doc:: drivers/gpu/vga/vga_switcheroo.c
:doc: Manual switching and manual power control
Driver power control
--------------------
.. kernel-doc:: drivers/gpu/vga/vga_switcheroo.c
:doc: Driver power control
API
===
Public functions
----------------
.. kernel-doc:: drivers/gpu/vga/vga_switcheroo.c
:export:
Public structures
-----------------
.. kernel-doc:: include/linux/vga_switcheroo.h
:functions: vga_switcheroo_handler
.. kernel-doc:: include/linux/vga_switcheroo.h
:functions: vga_switcheroo_client_ops
Public constants
----------------
.. kernel-doc:: include/linux/vga_switcheroo.h
:functions: vga_switcheroo_handler_flags_t
.. kernel-doc:: include/linux/vga_switcheroo.h
:functions: vga_switcheroo_client_id
.. kernel-doc:: include/linux/vga_switcheroo.h
:functions: vga_switcheroo_state
Private structures
------------------
.. kernel-doc:: drivers/gpu/vga/vga_switcheroo.c
:functions: vgasr_priv
.. kernel-doc:: drivers/gpu/vga/vga_switcheroo.c
:functions: vga_switcheroo_client
Handlers
========
apple-gmux Handler
------------------
.. kernel-doc:: drivers/platform/x86/apple-gmux.c
:doc: Overview
.. kernel-doc:: drivers/platform/x86/apple-gmux.c
:doc: Interrupt
Graphics mux
~~~~~~~~~~~~
.. kernel-doc:: drivers/platform/x86/apple-gmux.c
:doc: Graphics mux
Power control
~~~~~~~~~~~~~
.. kernel-doc:: drivers/platform/x86/apple-gmux.c
:doc: Power control
Backlight control
~~~~~~~~~~~~~~~~~
.. kernel-doc:: drivers/platform/x86/apple-gmux.c
:doc: Backlight control
Public functions
~~~~~~~~~~~~~~~~
.. kernel-doc:: include/linux/apple-gmux.h
:internal:
.. WARNING: DOCPROC directive not supported: !Cdrivers/gpu/vga/vga_switcheroo.c
.. WARNING: DOCPROC directive not supported: !Cinclude/linux/vga_switcheroo.h
.. WARNING: DOCPROC directive not supported: !Cdrivers/platform/x86/apple-gmux.c
...@@ -13,6 +13,7 @@ Contents: ...@@ -13,6 +13,7 @@ Contents:
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
gpu/index
Indices and tables Indices and tables
================== ==================
......
...@@ -3865,7 +3865,7 @@ F: drivers/gpu/vga/ ...@@ -3865,7 +3865,7 @@ F: drivers/gpu/vga/
F: Documentation/devicetree/bindings/display/ F: Documentation/devicetree/bindings/display/
F: Documentation/devicetree/bindings/gpu/ F: Documentation/devicetree/bindings/gpu/
F: Documentation/devicetree/bindings/video/ F: Documentation/devicetree/bindings/video/
F: Documentation/DocBook/gpu.* F: Documentation/gpu/
F: include/drm/ F: include/drm/
F: include/uapi/drm/ F: include/uapi/drm/
...@@ -3917,6 +3917,7 @@ S: Supported ...@@ -3917,6 +3917,7 @@ S: Supported
F: drivers/gpu/drm/i915/ F: drivers/gpu/drm/i915/
F: include/drm/i915* F: include/drm/i915*
F: include/uapi/drm/i915_drm.h F: include/uapi/drm/i915_drm.h
F: Documentation/gpu/i915.rst
DRM DRIVERS FOR ATMEL HLCDC DRM DRIVERS FOR ATMEL HLCDC
M: Boris Brezillon <boris.brezillon@free-electrons.com> M: Boris Brezillon <boris.brezillon@free-electrons.com>
......
...@@ -824,7 +824,7 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) ...@@ -824,7 +824,7 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
EXPORT_SYMBOL_GPL(dma_buf_vunmap); EXPORT_SYMBOL_GPL(dma_buf_vunmap);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
static int dma_buf_describe(struct seq_file *s) static int dma_buf_debug_show(struct seq_file *s, void *unused)
{ {
int ret; int ret;
struct dma_buf *buf_obj; struct dma_buf *buf_obj;
...@@ -879,17 +879,9 @@ static int dma_buf_describe(struct seq_file *s) ...@@ -879,17 +879,9 @@ static int dma_buf_describe(struct seq_file *s)
return 0; return 0;
} }
static int dma_buf_show(struct seq_file *s, void *unused)
{
void (*func)(struct seq_file *) = s->private;
func(s);
return 0;
}
static int dma_buf_debug_open(struct inode *inode, struct file *file) static int dma_buf_debug_open(struct inode *inode, struct file *file)
{ {
return single_open(file, dma_buf_show, inode->i_private); return single_open(file, dma_buf_debug_show, NULL);
} }
static const struct file_operations dma_buf_debug_fops = { static const struct file_operations dma_buf_debug_fops = {
...@@ -903,20 +895,23 @@ static struct dentry *dma_buf_debugfs_dir; ...@@ -903,20 +895,23 @@ static struct dentry *dma_buf_debugfs_dir;
static int dma_buf_init_debugfs(void) static int dma_buf_init_debugfs(void)
{ {
struct dentry *d;
int err = 0; int err = 0;
dma_buf_debugfs_dir = debugfs_create_dir("dma_buf", NULL); d = debugfs_create_dir("dma_buf", NULL);
if (IS_ERR(d))
if (IS_ERR(dma_buf_debugfs_dir)) { return PTR_ERR(d);
err = PTR_ERR(dma_buf_debugfs_dir);
dma_buf_debugfs_dir = NULL;
return err;
}
err = dma_buf_debugfs_create_file("bufinfo", dma_buf_describe); dma_buf_debugfs_dir = d;
if (err) d = debugfs_create_file("bufinfo", S_IRUGO, dma_buf_debugfs_dir,
NULL, &dma_buf_debug_fops);
if (IS_ERR(d)) {
pr_debug("dma_buf: debugfs: failed to create node bufinfo\n"); pr_debug("dma_buf: debugfs: failed to create node bufinfo\n");
debugfs_remove_recursive(dma_buf_debugfs_dir);
dma_buf_debugfs_dir = NULL;
err = PTR_ERR(d);
}
return err; return err;
} }
...@@ -926,17 +921,6 @@ static void dma_buf_uninit_debugfs(void) ...@@ -926,17 +921,6 @@ static void dma_buf_uninit_debugfs(void)
if (dma_buf_debugfs_dir) if (dma_buf_debugfs_dir)
debugfs_remove_recursive(dma_buf_debugfs_dir); debugfs_remove_recursive(dma_buf_debugfs_dir);
} }
int dma_buf_debugfs_create_file(const char *name,
int (*write)(struct seq_file *))
{
struct dentry *d;
d = debugfs_create_file(name, S_IRUGO, dma_buf_debugfs_dir,
write, &dma_buf_debug_fops);
return PTR_ERR_OR_ZERO(d);
}
#else #else
static inline int dma_buf_init_debugfs(void) static inline int dma_buf_init_debugfs(void)
{ {
......
...@@ -187,12 +187,12 @@ int init_pipelines(struct device_queue_manager *dqm, ...@@ -187,12 +187,12 @@ int init_pipelines(struct device_queue_manager *dqm,
unsigned int get_first_pipe(struct device_queue_manager *dqm); unsigned int get_first_pipe(struct device_queue_manager *dqm);
unsigned int get_pipes_num(struct device_queue_manager *dqm); unsigned int get_pipes_num(struct device_queue_manager *dqm);
extern inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd) static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd)
{ {
return (pdd->lds_base >> 16) & 0xFF; return (pdd->lds_base >> 16) & 0xFF;
} }
extern inline unsigned int static inline unsigned int
get_sh_mem_bases_nybble_64(struct kfd_process_device *pdd) get_sh_mem_bases_nybble_64(struct kfd_process_device *pdd)
{ {
return (pdd->lds_base >> 60) & 0x0E; return (pdd->lds_base >> 60) & 0x0E;
......
...@@ -617,10 +617,7 @@ int kgd2kfd_resume(struct kfd_dev *kfd); ...@@ -617,10 +617,7 @@ int kgd2kfd_resume(struct kfd_dev *kfd);
int kfd_init_apertures(struct kfd_process *process); int kfd_init_apertures(struct kfd_process *process);
/* Queue Context Management */ /* Queue Context Management */
inline uint32_t lower_32(uint64_t x);
inline uint32_t upper_32(uint64_t x);
struct cik_sdma_rlc_registers *get_sdma_mqd(void *mqd); struct cik_sdma_rlc_registers *get_sdma_mqd(void *mqd);
inline uint32_t get_sdma_base_addr(struct cik_sdma_rlc_registers *m);
int init_queue(struct queue **q, struct queue_properties properties); int init_queue(struct queue **q, struct queue_properties properties);
void uninit_queue(struct queue *q); void uninit_queue(struct queue *q);
......
...@@ -211,15 +211,8 @@ static int arcpgu_probe(struct platform_device *pdev) ...@@ -211,15 +211,8 @@ static int arcpgu_probe(struct platform_device *pdev)
if (ret) if (ret)
goto err_unload; goto err_unload;
ret = drm_connector_register_all(drm);
if (ret)
goto err_unregister;
return 0; return 0;
err_unregister:
drm_dev_unregister(drm);
err_unload: err_unload:
arcpgu_unload(drm); arcpgu_unload(drm);
...@@ -233,7 +226,6 @@ static int arcpgu_remove(struct platform_device *pdev) ...@@ -233,7 +226,6 @@ static int arcpgu_remove(struct platform_device *pdev)
{ {
struct drm_device *drm = platform_get_drvdata(pdev); struct drm_device *drm = platform_get_drvdata(pdev);
drm_connector_unregister_all(drm);
drm_dev_unregister(drm); drm_dev_unregister(drm);
arcpgu_unload(drm); arcpgu_unload(drm);
drm_dev_unref(drm); drm_dev_unref(drm);
......
...@@ -189,7 +189,6 @@ static struct drm_driver armada_drm_driver = { ...@@ -189,7 +189,6 @@ static struct drm_driver armada_drm_driver = {
.load = armada_drm_load, .load = armada_drm_load,
.lastclose = armada_drm_lastclose, .lastclose = armada_drm_lastclose,
.unload = armada_drm_unload, .unload = armada_drm_unload,
.set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_no_hw_counter, .get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = armada_drm_enable_vblank, .enable_vblank = armada_drm_enable_vblank,
.disable_vblank = armada_drm_disable_vblank, .disable_vblank = armada_drm_disable_vblank,
......
...@@ -121,6 +121,7 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, ...@@ -121,6 +121,7 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
int ret; int ret;
ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip, ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip,
BIT(DRM_ROTATE_0),
0, INT_MAX, true, false, &visible); 0, INT_MAX, true, false, &visible);
if (ret) if (ret)
return ret; return ret;
......
...@@ -691,13 +691,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev) ...@@ -691,13 +691,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev)
destroy_workqueue(dc->wq); destroy_workqueue(dc->wq);
} }
static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev)
{
mutex_lock(&dev->mode_config.mutex);
drm_connector_unregister_all(dev);
mutex_unlock(&dev->mode_config.mutex);
}
static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) static void atmel_hlcdc_dc_lastclose(struct drm_device *dev)
{ {
struct atmel_hlcdc_dc *dc = dev->dev_private; struct atmel_hlcdc_dc *dc = dev->dev_private;
...@@ -815,15 +808,8 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) ...@@ -815,15 +808,8 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
if (ret) if (ret)
goto err_unload; goto err_unload;
ret = drm_connector_register_all(ddev);
if (ret)
goto err_unregister;
return 0; return 0;
err_unregister:
drm_dev_unregister(ddev);
err_unload: err_unload:
atmel_hlcdc_dc_unload(ddev); atmel_hlcdc_dc_unload(ddev);
...@@ -837,7 +823,6 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) ...@@ -837,7 +823,6 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
{ {
struct drm_device *ddev = platform_get_drvdata(pdev); struct drm_device *ddev = platform_get_drvdata(pdev);
atmel_hlcdc_dc_connector_unplug_all(ddev);
drm_dev_unregister(ddev); drm_dev_unregister(ddev);
atmel_hlcdc_dc_unload(ddev); atmel_hlcdc_dc_unload(ddev);
drm_dev_unref(ddev); drm_dev_unref(ddev);
......
...@@ -30,25 +30,36 @@ ...@@ -30,25 +30,36 @@
#include <drm/drmP.h> #include <drm/drmP.h>
#include "drm_internal.h" #include "drm_internal.h"
#include "drm_legacy.h"
/** /**
* drm_getmagic - Get unique magic of a client * DOC: master and authentication
* @dev: DRM device to operate on
* @data: ioctl data containing the drm_auth object
* @file_priv: DRM file that performs the operation
* *
* This looks up the unique magic of the passed client and returns it. If the * struct &drm_master is used to track groups of clients with open
* client did not have a magic assigned, yet, a new one is registered. The magic * primary/legacy device nodes. For every struct &drm_file which has had at
* is stored in the passed drm_auth object. * least once successfully became the device master (either through the
* SET_MASTER IOCTL, or implicitly through opening the primary device node when
* no one else is the current master that time) there exists one &drm_master.
* This is noted in the is_master member of &drm_file. All other clients have
* just a pointer to the &drm_master they are associated with.
* *
* Returns: 0 on success, negative error code on failure. * In addition only one &drm_master can be the current master for a &drm_device.
* It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or
* implicitly through closing/openeing the primary device node. See also
* drm_is_current_master().
*
* Clients can authenticate against the current master (if it matches their own)
* using the GETMAGIC and AUTHMAGIC IOCTLs. Together with exchanging masters,
* this allows controlled access to the device for an entire group of mutually
* trusted clients.
*/ */
int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
{ {
struct drm_auth *auth = data; struct drm_auth *auth = data;
int ret = 0; int ret = 0;
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->master_mutex);
if (!file_priv->magic) { if (!file_priv->magic) {
ret = idr_alloc(&file_priv->master->magic_map, file_priv, ret = idr_alloc(&file_priv->master->magic_map, file_priv,
1, 0, GFP_KERNEL); 1, 0, GFP_KERNEL);
...@@ -56,23 +67,13 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) ...@@ -56,23 +67,13 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
file_priv->magic = ret; file_priv->magic = ret;
} }
auth->magic = file_priv->magic; auth->magic = file_priv->magic;
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->master_mutex);
DRM_DEBUG("%u\n", auth->magic); DRM_DEBUG("%u\n", auth->magic);
return ret < 0 ? ret : 0; return ret < 0 ? ret : 0;
} }
/**
* drm_authmagic - Authenticate client with a magic
* @dev: DRM device to operate on
* @data: ioctl data containing the drm_auth object
* @file_priv: DRM file that performs the operation
*
* This looks up a DRM client by the passed magic and authenticates it.
*
* Returns: 0 on success, negative error code on failure.
*/
int drm_authmagic(struct drm_device *dev, void *data, int drm_authmagic(struct drm_device *dev, void *data,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
...@@ -81,13 +82,253 @@ int drm_authmagic(struct drm_device *dev, void *data, ...@@ -81,13 +82,253 @@ int drm_authmagic(struct drm_device *dev, void *data,
DRM_DEBUG("%u\n", auth->magic); DRM_DEBUG("%u\n", auth->magic);
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->master_mutex);
file = idr_find(&file_priv->master->magic_map, auth->magic); file = idr_find(&file_priv->master->magic_map, auth->magic);
if (file) { if (file) {
file->authenticated = 1; file->authenticated = 1;
idr_replace(&file_priv->master->magic_map, NULL, auth->magic); idr_replace(&file_priv->master->magic_map, NULL, auth->magic);
} }
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->master_mutex);
return file ? 0 : -EINVAL; return file ? 0 : -EINVAL;
} }
static struct drm_master *drm_master_create(struct drm_device *dev)
{
struct drm_master *master;
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return NULL;
kref_init(&master->refcount);
spin_lock_init(&master->lock.spinlock);
init_waitqueue_head(&master->lock.lock_queue);
idr_init(&master->magic_map);
master->dev = dev;
return master;
}
static int drm_set_master(struct drm_device *dev, struct drm_file *fpriv,
bool new_master)
{
int ret = 0;
dev->master = drm_master_get(fpriv->master);
if (dev->driver->master_set) {
ret = dev->driver->master_set(dev, fpriv, new_master);
if (unlikely(ret != 0)) {
drm_master_put(&dev->master);
}
}
return ret;
}
static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
{
struct drm_master *old_master;
int ret;
lockdep_assert_held_once(&dev->master_mutex);
old_master = fpriv->master;
fpriv->master = drm_master_create(dev);
if (!fpriv->master) {
fpriv->master = old_master;
return -ENOMEM;
}
if (dev->driver->master_create) {
ret = dev->driver->master_create(dev, fpriv->master);
if (ret)
goto out_err;
}
fpriv->is_master = 1;
fpriv->authenticated = 1;
ret = drm_set_master(dev, fpriv, true);
if (ret)
goto out_err;
if (old_master)
drm_master_put(&old_master);
return 0;
out_err:
/* drop references and restore old master on failure */
drm_master_put(&fpriv->master);
fpriv->master = old_master;
return ret;
}
int drm_setmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
int ret = 0;
mutex_lock(&dev->master_mutex);
if (drm_is_current_master(file_priv))
goto out_unlock;
if (dev->master) {
ret = -EINVAL;
goto out_unlock;
}
if (!file_priv->master) {
ret = -EINVAL;
goto out_unlock;
}
if (!file_priv->is_master) {
ret = drm_new_set_master(dev, file_priv);
goto out_unlock;
}
ret = drm_set_master(dev, file_priv, false);
out_unlock:
mutex_unlock(&dev->master_mutex);
return ret;
}
static void drm_drop_master(struct drm_device *dev,
struct drm_file *fpriv)
{
if (dev->driver->master_drop)
dev->driver->master_drop(dev, fpriv);
drm_master_put(&dev->master);
}
int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
int ret = -EINVAL;
mutex_lock(&dev->master_mutex);
if (!drm_is_current_master(file_priv))
goto out_unlock;
if (!dev->master)
goto out_unlock;
ret = 0;
drm_drop_master(dev, file_priv);
out_unlock:
mutex_unlock(&dev->master_mutex);
return ret;
}
int drm_master_open(struct drm_file *file_priv)
{
struct drm_device *dev = file_priv->minor->dev;
int ret = 0;
/* if there is no current master make this fd it, but do not create
* any master object for render clients */
mutex_lock(&dev->master_mutex);
if (!dev->master)
ret = drm_new_set_master(dev, file_priv);
else
file_priv->master = drm_master_get(dev->master);
mutex_unlock(&dev->master_mutex);
return ret;
}
void drm_master_release(struct drm_file *file_priv)
{
struct drm_device *dev = file_priv->minor->dev;
struct drm_master *master = file_priv->master;
mutex_lock(&dev->master_mutex);
if (file_priv->magic)
idr_remove(&file_priv->master->magic_map, file_priv->magic);
if (!drm_is_current_master(file_priv))
goto out;
if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
/*
* Since the master is disappearing, so is the
* possibility to lock.
*/
mutex_lock(&dev->struct_mutex);
if (master->lock.hw_lock) {
if (dev->sigdata.lock == master->lock.hw_lock)
dev->sigdata.lock = NULL;
master->lock.hw_lock = NULL;
master->lock.file_priv = NULL;
wake_up_interruptible_all(&master->lock.lock_queue);
}
mutex_unlock(&dev->struct_mutex);
}
if (dev->master == file_priv->master)
drm_drop_master(dev, file_priv);
out:
/* drop the master reference held by the file priv */
if (file_priv->master)
drm_master_put(&file_priv->master);
mutex_unlock(&dev->master_mutex);
}
/**
* drm_is_current_master - checks whether @priv is the current master
* @fpriv: DRM file private
*
* Checks whether @fpriv is current master on its device. This decides whether a
* client is allowed to run DRM_MASTER IOCTLs.
*
* Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting
* - the current master is assumed to own the non-shareable display hardware.
*/
bool drm_is_current_master(struct drm_file *fpriv)
{
return fpriv->is_master && fpriv->master == fpriv->minor->dev->master;
}
EXPORT_SYMBOL(drm_is_current_master);
/**
* drm_master_get - reference a master pointer
* @master: struct &drm_master
*
* Increments the reference count of @master and returns a pointer to @master.
*/
struct drm_master *drm_master_get(struct drm_master *master)
{
kref_get(&master->refcount);
return master;
}
EXPORT_SYMBOL(drm_master_get);
static void drm_master_destroy(struct kref *kref)
{
struct drm_master *master = container_of(kref, struct drm_master, refcount);
struct drm_device *dev = master->dev;
if (dev->driver->master_destroy)
dev->driver->master_destroy(dev, master);
drm_legacy_master_rmmaps(dev, master);
idr_destroy(&master->magic_map);
kfree(master->unique);
kfree(master);
}
/**
* drm_master_put - unreference and clear a master pointer
* @master: pointer to a pointer of struct &drm_master
*
* This decrements the &drm_master behind @master and sets it to NULL.
*/
void drm_master_put(struct drm_master **master)
{
kref_put(&(*master)->refcount, drm_master_destroy);
*master = NULL;
}
EXPORT_SYMBOL(drm_master_put);
...@@ -51,7 +51,7 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev, ...@@ -51,7 +51,7 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
*/ */
if (!entry->map || if (!entry->map ||
map->type != entry->map->type || map->type != entry->map->type ||
entry->master != dev->primary->master) entry->master != dev->master)
continue; continue;
switch (map->type) { switch (map->type) {
case _DRM_SHM: case _DRM_SHM:
...@@ -245,12 +245,12 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, ...@@ -245,12 +245,12 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
map->offset = (unsigned long)map->handle; map->offset = (unsigned long)map->handle;
if (map->flags & _DRM_CONTAINS_LOCK) { if (map->flags & _DRM_CONTAINS_LOCK) {
/* Prevent a 2nd X Server from creating a 2nd lock */ /* Prevent a 2nd X Server from creating a 2nd lock */
if (dev->primary->master->lock.hw_lock != NULL) { if (dev->master->lock.hw_lock != NULL) {
vfree(map->handle); vfree(map->handle);
kfree(map); kfree(map);
return -EBUSY; return -EBUSY;
} }
dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle; /* Pointer to lock */ dev->sigdata.lock = dev->master->lock.hw_lock = map->handle; /* Pointer to lock */
} }
break; break;
case _DRM_AGP: { case _DRM_AGP: {
...@@ -356,7 +356,7 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, ...@@ -356,7 +356,7 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
if (!(map->flags & _DRM_DRIVER)) if (!(map->flags & _DRM_DRIVER))
list->master = dev->primary->master; list->master = dev->master;
*maplist = list; *maplist = list;
return 0; return 0;
} }
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <drm/drm_fourcc.h> #include <drm/drm_fourcc.h>
#include <drm/drm_modeset_lock.h> #include <drm/drm_modeset_lock.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_auth.h>
#include "drm_crtc_internal.h" #include "drm_crtc_internal.h"
#include "drm_internal.h" #include "drm_internal.h"
...@@ -608,6 +609,31 @@ static unsigned int drm_num_crtcs(struct drm_device *dev) ...@@ -608,6 +609,31 @@ static unsigned int drm_num_crtcs(struct drm_device *dev)
return num; return num;
} }
static int drm_crtc_register_all(struct drm_device *dev)
{
struct drm_crtc *crtc;
int ret = 0;
drm_for_each_crtc(crtc, dev) {
if (crtc->funcs->late_register)
ret = crtc->funcs->late_register(crtc);
if (ret)
return ret;
}
return 0;
}
static void drm_crtc_unregister_all(struct drm_device *dev)
{
struct drm_crtc *crtc;
drm_for_each_crtc(crtc, dev) {
if (crtc->funcs->early_unregister)
crtc->funcs->early_unregister(crtc);
}
}
/** /**
* drm_crtc_init_with_planes - Initialise a new CRTC object with * drm_crtc_init_with_planes - Initialise a new CRTC object with
* specified primary and cursor planes. * specified primary and cursor planes.
...@@ -938,6 +964,12 @@ void drm_connector_cleanup(struct drm_connector *connector) ...@@ -938,6 +964,12 @@ void drm_connector_cleanup(struct drm_connector *connector)
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
struct drm_display_mode *mode, *t; struct drm_display_mode *mode, *t;
/* The connector should have been removed from userspace long before
* it is finally destroyed.
*/
if (WARN_ON(connector->registered))
drm_connector_unregister(connector);
if (connector->tile_group) { if (connector->tile_group) {
drm_mode_put_tile_group(dev, connector->tile_group); drm_mode_put_tile_group(dev, connector->tile_group);
connector->tile_group = NULL; connector->tile_group = NULL;
...@@ -984,19 +1016,34 @@ int drm_connector_register(struct drm_connector *connector) ...@@ -984,19 +1016,34 @@ int drm_connector_register(struct drm_connector *connector)
{ {
int ret; int ret;
if (connector->registered)
return 0;
ret = drm_sysfs_connector_add(connector); ret = drm_sysfs_connector_add(connector);
if (ret) if (ret)
return ret; return ret;
ret = drm_debugfs_connector_add(connector); ret = drm_debugfs_connector_add(connector);
if (ret) { if (ret) {
drm_sysfs_connector_remove(connector); goto err_sysfs;
return ret; }
if (connector->funcs->late_register) {
ret = connector->funcs->late_register(connector);
if (ret)
goto err_debugfs;
} }
drm_mode_object_register(connector->dev, &connector->base); drm_mode_object_register(connector->dev, &connector->base);
connector->registered = true;
return 0; return 0;
err_debugfs:
drm_debugfs_connector_remove(connector);
err_sysfs:
drm_sysfs_connector_remove(connector);
return ret;
} }
EXPORT_SYMBOL(drm_connector_register); EXPORT_SYMBOL(drm_connector_register);
...@@ -1008,8 +1055,16 @@ EXPORT_SYMBOL(drm_connector_register); ...@@ -1008,8 +1055,16 @@ EXPORT_SYMBOL(drm_connector_register);
*/ */
void drm_connector_unregister(struct drm_connector *connector) void drm_connector_unregister(struct drm_connector *connector)
{ {
if (!connector->registered)
return;
if (connector->funcs->early_unregister)
connector->funcs->early_unregister(connector);
drm_sysfs_connector_remove(connector); drm_sysfs_connector_remove(connector);
drm_debugfs_connector_remove(connector); drm_debugfs_connector_remove(connector);
connector->registered = false;
} }
EXPORT_SYMBOL(drm_connector_unregister); EXPORT_SYMBOL(drm_connector_unregister);
...@@ -1018,9 +1073,9 @@ EXPORT_SYMBOL(drm_connector_unregister); ...@@ -1018,9 +1073,9 @@ EXPORT_SYMBOL(drm_connector_unregister);
* @dev: drm device * @dev: drm device
* *
* This function registers all connectors in sysfs and other places so that * This function registers all connectors in sysfs and other places so that
* userspace can start to access them. Drivers can call it after calling * userspace can start to access them. drm_connector_register_all() is called
* drm_dev_register() to complete the device registration, if they don't call * automatically from drm_dev_register() to complete the device registration,
* drm_connector_register() on each connector individually. * if they don't call drm_connector_register() on each connector individually.
* *
* When a device is unplugged and should be removed from userspace access, * When a device is unplugged and should be removed from userspace access,
* call drm_connector_unregister_all(), which is the inverse of this * call drm_connector_unregister_all(), which is the inverse of this
...@@ -1073,6 +1128,31 @@ void drm_connector_unregister_all(struct drm_device *dev) ...@@ -1073,6 +1128,31 @@ void drm_connector_unregister_all(struct drm_device *dev)
} }
EXPORT_SYMBOL(drm_connector_unregister_all); EXPORT_SYMBOL(drm_connector_unregister_all);
static int drm_encoder_register_all(struct drm_device *dev)
{
struct drm_encoder *encoder;
int ret = 0;
drm_for_each_encoder(encoder, dev) {
if (encoder->funcs->late_register)
ret = encoder->funcs->late_register(encoder);
if (ret)
return ret;
}
return 0;
}
static void drm_encoder_unregister_all(struct drm_device *dev)
{
struct drm_encoder *encoder;
drm_for_each_encoder(encoder, dev) {
if (encoder->funcs->early_unregister)
encoder->funcs->early_unregister(encoder);
}
}
/** /**
* drm_encoder_init - Init a preallocated encoder * drm_encoder_init - Init a preallocated encoder
* @dev: drm device * @dev: drm device
...@@ -1261,6 +1341,31 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, ...@@ -1261,6 +1341,31 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
} }
EXPORT_SYMBOL(drm_universal_plane_init); EXPORT_SYMBOL(drm_universal_plane_init);
static int drm_plane_register_all(struct drm_device *dev)
{
struct drm_plane *plane;
int ret = 0;
drm_for_each_plane(plane, dev) {
if (plane->funcs->late_register)
ret = plane->funcs->late_register(plane);
if (ret)
return ret;
}
return 0;
}
static void drm_plane_unregister_all(struct drm_device *dev)
{
struct drm_plane *plane;
drm_for_each_plane(plane, dev) {
if (plane->funcs->early_unregister)
plane->funcs->early_unregister(plane);
}
}
/** /**
* drm_plane_init - Initialize a legacy plane * drm_plane_init - Initialize a legacy plane
* @dev: DRM device * @dev: DRM device
...@@ -1383,6 +1488,46 @@ void drm_plane_force_disable(struct drm_plane *plane) ...@@ -1383,6 +1488,46 @@ void drm_plane_force_disable(struct drm_plane *plane)
} }
EXPORT_SYMBOL(drm_plane_force_disable); EXPORT_SYMBOL(drm_plane_force_disable);
int drm_modeset_register_all(struct drm_device *dev)
{
int ret;
ret = drm_plane_register_all(dev);
if (ret)
goto err_plane;
ret = drm_crtc_register_all(dev);
if (ret)
goto err_crtc;
ret = drm_encoder_register_all(dev);
if (ret)
goto err_encoder;
ret = drm_connector_register_all(dev);
if (ret)
goto err_connector;
return 0;
err_connector:
drm_encoder_unregister_all(dev);
err_encoder:
drm_crtc_unregister_all(dev);
err_crtc:
drm_plane_unregister_all(dev);
err_plane:
return ret;
}
void drm_modeset_unregister_all(struct drm_device *dev)
{
drm_connector_unregister_all(dev);
drm_encoder_unregister_all(dev);
drm_crtc_unregister_all(dev);
drm_plane_unregister_all(dev);
}
static int drm_mode_create_standard_properties(struct drm_device *dev) static int drm_mode_create_standard_properties(struct drm_device *dev)
{ {
struct drm_property *prop; struct drm_property *prop;
...@@ -3499,7 +3644,7 @@ int drm_mode_getfb(struct drm_device *dev, ...@@ -3499,7 +3644,7 @@ int drm_mode_getfb(struct drm_device *dev,
r->bpp = fb->bits_per_pixel; r->bpp = fb->bits_per_pixel;
r->pitch = fb->pitches[0]; r->pitch = fb->pitches[0];
if (fb->funcs->create_handle) { if (fb->funcs->create_handle) {
if (file_priv->is_master || capable(CAP_SYS_ADMIN) || if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) ||
drm_is_control_client(file_priv)) { drm_is_control_client(file_priv)) {
ret = fb->funcs->create_handle(fb, file_priv, ret = fb->funcs->create_handle(fb, file_priv,
&r->handle); &r->handle);
...@@ -3656,6 +3801,13 @@ void drm_fb_release(struct drm_file *priv) ...@@ -3656,6 +3801,13 @@ void drm_fb_release(struct drm_file *priv)
} }
} }
static bool drm_property_type_valid(struct drm_property *property)
{
if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
}
/** /**
* drm_property_create - create a new property type * drm_property_create - create a new property type
* @dev: drm device * @dev: drm device
......
...@@ -232,6 +232,9 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev) ...@@ -232,6 +232,9 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev)
*/ */
void drm_helper_disable_unused_functions(struct drm_device *dev) void drm_helper_disable_unused_functions(struct drm_device *dev)
{ {
if (drm_core_check_feature(dev, DRIVER_ATOMIC))
DRM_ERROR("Called for atomic driver, this is not what you want.\n");
drm_modeset_lock_all(dev); drm_modeset_lock_all(dev);
__drm_helper_disable_unused_functions(dev); __drm_helper_disable_unused_functions(dev);
drm_modeset_unlock_all(dev); drm_modeset_unlock_all(dev);
......
...@@ -31,14 +31,100 @@ ...@@ -31,14 +31,100 @@
* and are not exported to drivers. * and are not exported to drivers.
*/ */
/* drm_crtc.c */
void drm_connector_ida_init(void);
void drm_connector_ida_destroy(void);
int drm_mode_object_get(struct drm_device *dev, int drm_mode_object_get(struct drm_device *dev,
struct drm_mode_object *obj, uint32_t obj_type); struct drm_mode_object *obj, uint32_t obj_type);
void drm_mode_object_unregister(struct drm_device *dev, void drm_mode_object_unregister(struct drm_device *dev,
struct drm_mode_object *object); struct drm_mode_object *object);
bool drm_property_change_valid_get(struct drm_property *property,
uint64_t value,
struct drm_mode_object **ref);
void drm_property_change_valid_put(struct drm_property *property,
struct drm_mode_object *ref);
int drm_plane_check_pixel_format(const struct drm_plane *plane,
u32 format);
int drm_crtc_check_viewport(const struct drm_crtc *crtc,
int x, int y,
const struct drm_display_mode *mode,
const struct drm_framebuffer *fb);
void drm_fb_release(struct drm_file *file_priv);
void drm_property_destroy_user_blobs(struct drm_device *dev,
struct drm_file *file_priv);
/* dumb buffer support IOCTLs */
int drm_mode_create_dumb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
/* framebuffer IOCTLs */
extern int drm_mode_addfb(struct drm_device *dev,
void *data, struct drm_file *file_priv);
extern int drm_mode_addfb2(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_rmfb(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_getfb(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
/* IOCTLs */
int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_mode_getresources(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_getplane_res(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_mode_getcrtc(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_getconnector(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_setcrtc(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_getplane(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_setplane(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_cursor_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_cursor2_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_getproperty_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_getblob_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_createblob_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_destroyblob_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_getencoder(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_gamma_get_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_gamma_set_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_page_flip_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
/* drm_atomic.c */ /* drm_atomic.c */
int drm_atomic_get_property(struct drm_mode_object *obj, int drm_atomic_get_property(struct drm_mode_object *obj,
struct drm_property *property, uint64_t *val); struct drm_property *property, uint64_t *val);
int drm_mode_atomic_ioctl(struct drm_device *dev, int drm_mode_atomic_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv); void *data, struct drm_file *file_priv);
int drm_modeset_register_all(struct drm_device *dev);
void drm_modeset_unregister_all(struct drm_device *dev);
...@@ -46,11 +46,8 @@ ...@@ -46,11 +46,8 @@
static const struct drm_info_list drm_debugfs_list[] = { static const struct drm_info_list drm_debugfs_list[] = {
{"name", drm_name_info, 0}, {"name", drm_name_info, 0},
{"vm", drm_vm_info, 0},
{"clients", drm_clients_info, 0}, {"clients", drm_clients_info, 0},
{"bufs", drm_bufs_info, 0},
{"gem_names", drm_gem_name_info, DRIVER_GEM}, {"gem_names", drm_gem_name_info, DRIVER_GEM},
{"vma", drm_vma_info, 0},
}; };
#define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list) #define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list)
......
...@@ -708,8 +708,6 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, ...@@ -708,8 +708,6 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
mutex_lock(&aux->hw_mutex);
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
msg.address = msgs[i].addr; msg.address = msgs[i].addr;
drm_dp_i2c_msg_set_request(&msg, &msgs[i]); drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
...@@ -764,8 +762,6 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, ...@@ -764,8 +762,6 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
msg.size = 0; msg.size = 0;
(void)drm_dp_i2c_do_msg(aux, &msg); (void)drm_dp_i2c_do_msg(aux, &msg);
mutex_unlock(&aux->hw_mutex);
return err; return err;
} }
...@@ -774,22 +770,64 @@ static const struct i2c_algorithm drm_dp_i2c_algo = { ...@@ -774,22 +770,64 @@ static const struct i2c_algorithm drm_dp_i2c_algo = {
.master_xfer = drm_dp_i2c_xfer, .master_xfer = drm_dp_i2c_xfer,
}; };
static struct drm_dp_aux *i2c_to_aux(struct i2c_adapter *i2c)
{
return container_of(i2c, struct drm_dp_aux, ddc);
}
static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
{
mutex_lock(&i2c_to_aux(i2c)->hw_mutex);
}
static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
{
return mutex_trylock(&i2c_to_aux(i2c)->hw_mutex);
}
static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
{
mutex_unlock(&i2c_to_aux(i2c)->hw_mutex);
}
/** /**
* drm_dp_aux_register() - initialise and register aux channel * drm_dp_aux_init() - minimally initialise an aux channel
* @aux: DisplayPort AUX channel * @aux: DisplayPort AUX channel
* *
* Returns 0 on success or a negative error code on failure. * If you need to use the drm_dp_aux's i2c adapter prior to registering it
* with the outside world, call drm_dp_aux_init() first. You must still
* call drm_dp_aux_register() once the connector has been registered to
* allow userspace access to the auxiliary DP channel.
*/ */
int drm_dp_aux_register(struct drm_dp_aux *aux) void drm_dp_aux_init(struct drm_dp_aux *aux)
{ {
int ret;
mutex_init(&aux->hw_mutex); mutex_init(&aux->hw_mutex);
aux->ddc.algo = &drm_dp_i2c_algo; aux->ddc.algo = &drm_dp_i2c_algo;
aux->ddc.algo_data = aux; aux->ddc.algo_data = aux;
aux->ddc.retries = 3; aux->ddc.retries = 3;
aux->ddc.lock_bus = lock_bus;
aux->ddc.trylock_bus = trylock_bus;
aux->ddc.unlock_bus = unlock_bus;
}
EXPORT_SYMBOL(drm_dp_aux_init);
/**
* drm_dp_aux_register() - initialise and register aux channel
* @aux: DisplayPort AUX channel
*
* Automatically calls drm_dp_aux_init() if this hasn't been done yet.
*
* Returns 0 on success or a negative error code on failure.
*/
int drm_dp_aux_register(struct drm_dp_aux *aux)
{
int ret;
if (!aux->ddc.algo)
drm_dp_aux_init(aux);
aux->ddc.class = I2C_CLASS_DDC; aux->ddc.class = I2C_CLASS_DDC;
aux->ddc.owner = THIS_MODULE; aux->ddc.owner = THIS_MODULE;
aux->ddc.dev.parent = aux->dev; aux->ddc.dev.parent = aux->dev;
......
...@@ -34,8 +34,10 @@ ...@@ -34,8 +34,10 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_core.h> #include <drm/drm_core.h>
#include "drm_crtc_internal.h"
#include "drm_legacy.h" #include "drm_legacy.h"
#include "drm_internal.h" #include "drm_internal.h"
#include "drm_crtc_internal.h"
/* /*
* drm_debug: Enable debug output. * drm_debug: Enable debug output.
...@@ -93,114 +95,6 @@ void drm_ut_debug_printk(const char *function_name, const char *format, ...) ...@@ -93,114 +95,6 @@ void drm_ut_debug_printk(const char *function_name, const char *format, ...)
} }
EXPORT_SYMBOL(drm_ut_debug_printk); EXPORT_SYMBOL(drm_ut_debug_printk);
struct drm_master *drm_master_create(struct drm_minor *minor)
{
struct drm_master *master;
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return NULL;
kref_init(&master->refcount);
spin_lock_init(&master->lock.spinlock);
init_waitqueue_head(&master->lock.lock_queue);
idr_init(&master->magic_map);
master->minor = minor;
return master;
}
struct drm_master *drm_master_get(struct drm_master *master)
{
kref_get(&master->refcount);
return master;
}
EXPORT_SYMBOL(drm_master_get);
static void drm_master_destroy(struct kref *kref)
{
struct drm_master *master = container_of(kref, struct drm_master, refcount);
struct drm_device *dev = master->minor->dev;
if (dev->driver->master_destroy)
dev->driver->master_destroy(dev, master);
drm_legacy_master_rmmaps(dev, master);
idr_destroy(&master->magic_map);
kfree(master->unique);
kfree(master);
}
void drm_master_put(struct drm_master **master)
{
kref_put(&(*master)->refcount, drm_master_destroy);
*master = NULL;
}
EXPORT_SYMBOL(drm_master_put);
int drm_setmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
int ret = 0;
mutex_lock(&dev->master_mutex);
if (file_priv->is_master)
goto out_unlock;
if (file_priv->minor->master) {
ret = -EINVAL;
goto out_unlock;
}
if (!file_priv->master) {
ret = -EINVAL;
goto out_unlock;
}
if (!file_priv->allowed_master) {
ret = drm_new_set_master(dev, file_priv);
goto out_unlock;
}
file_priv->minor->master = drm_master_get(file_priv->master);
file_priv->is_master = 1;
if (dev->driver->master_set) {
ret = dev->driver->master_set(dev, file_priv, false);
if (unlikely(ret != 0)) {
file_priv->is_master = 0;
drm_master_put(&file_priv->minor->master);
}
}
out_unlock:
mutex_unlock(&dev->master_mutex);
return ret;
}
int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
int ret = -EINVAL;
mutex_lock(&dev->master_mutex);
if (!file_priv->is_master)
goto out_unlock;
if (!file_priv->minor->master)
goto out_unlock;
ret = 0;
if (dev->driver->master_drop)
dev->driver->master_drop(dev, file_priv, false);
drm_master_put(&file_priv->minor->master);
file_priv->is_master = 0;
out_unlock:
mutex_unlock(&dev->master_mutex);
return ret;
}
/* /*
* DRM Minors * DRM Minors
* A DRM device can provide several char-dev interfaces on the DRM-Major. Each * A DRM device can provide several char-dev interfaces on the DRM-Major. Each
...@@ -405,10 +299,9 @@ void drm_minor_release(struct drm_minor *minor) ...@@ -405,10 +299,9 @@ void drm_minor_release(struct drm_minor *minor)
* callbacks implemented by the driver. The driver then needs to initialize all * callbacks implemented by the driver. The driver then needs to initialize all
* the various subsystems for the drm device like memory management, vblank * the various subsystems for the drm device like memory management, vblank
* handling, modesetting support and intial output configuration plus obviously * handling, modesetting support and intial output configuration plus obviously
* initialize all the corresponding hardware bits. An important part of this is * initialize all the corresponding hardware bits. Finally when everything is up
* also calling drm_dev_set_unique() to set the userspace-visible unique name of * and running and ready for userspace the device instance can be published
* this device instance. Finally when everything is up and running and ready for * using drm_dev_register().
* userspace the device instance can be published using drm_dev_register().
* *
* There is also deprecated support for initalizing device instances using * There is also deprecated support for initalizing device instances using
* bus-specific helpers and the ->load() callback. But due to * bus-specific helpers and the ->load() callback. But due to
...@@ -430,6 +323,14 @@ void drm_minor_release(struct drm_minor *minor) ...@@ -430,6 +323,14 @@ void drm_minor_release(struct drm_minor *minor)
* dev_priv field of &drm_device. * dev_priv field of &drm_device.
*/ */
static int drm_dev_set_unique(struct drm_device *dev, const char *name)
{
kfree(dev->unique);
dev->unique = kstrdup(name, GFP_KERNEL);
return dev->unique ? 0 : -ENOMEM;
}
/** /**
* drm_put_dev - Unregister and release a DRM device * drm_put_dev - Unregister and release a DRM device
* @dev: DRM device * @dev: DRM device
...@@ -549,11 +450,12 @@ static void drm_fs_inode_free(struct inode *inode) ...@@ -549,11 +450,12 @@ static void drm_fs_inode_free(struct inode *inode)
} }
/** /**
* drm_dev_alloc - Allocate new DRM device * drm_dev_init - Initialise new DRM device
* @driver: DRM driver to allocate device for * @dev: DRM device
* @driver: DRM driver
* @parent: Parent device object * @parent: Parent device object
* *
* Allocate and initialize a new DRM device. No device registration is done. * Initialize a new DRM device. No device registration is done.
* Call drm_dev_register() to advertice the device to user space and register it * Call drm_dev_register() to advertice the device to user space and register it
* with other core subsystems. This should be done last in the device * with other core subsystems. This should be done last in the device
* initialization sequence to make sure userspace can't access an inconsistent * initialization sequence to make sure userspace can't access an inconsistent
...@@ -564,19 +466,18 @@ static void drm_fs_inode_free(struct inode *inode) ...@@ -564,19 +466,18 @@ static void drm_fs_inode_free(struct inode *inode)
* *
* Note that for purely virtual devices @parent can be NULL. * Note that for purely virtual devices @parent can be NULL.
* *
* Drivers that do not want to allocate their own device struct
* embedding struct &drm_device can call drm_dev_alloc() instead.
*
* RETURNS: * RETURNS:
* Pointer to new DRM device, or NULL if out of memory. * 0 on success, or error code on failure.
*/ */
struct drm_device *drm_dev_alloc(struct drm_driver *driver, int drm_dev_init(struct drm_device *dev,
struct device *parent) struct drm_driver *driver,
struct device *parent)
{ {
struct drm_device *dev;
int ret; int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
kref_init(&dev->ref); kref_init(&dev->ref);
dev->dev = parent; dev->dev = parent;
dev->driver = driver; dev->driver = driver;
...@@ -617,7 +518,8 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, ...@@ -617,7 +518,8 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
if (ret) if (ret)
goto err_minors; goto err_minors;
if (drm_ht_create(&dev->map_hash, 12)) ret = drm_ht_create(&dev->map_hash, 12);
if (ret)
goto err_minors; goto err_minors;
drm_legacy_ctxbitmap_init(dev); drm_legacy_ctxbitmap_init(dev);
...@@ -630,13 +532,13 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, ...@@ -630,13 +532,13 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
} }
} }
if (parent) { /* Use the parent device name as DRM device unique identifier, but fall
ret = drm_dev_set_unique(dev, dev_name(parent)); * back to the driver name for virtual devices like vgem. */
if (ret) ret = drm_dev_set_unique(dev, parent ? dev_name(parent) : driver->name);
goto err_setunique; if (ret)
} goto err_setunique;
return dev; return 0;
err_setunique: err_setunique:
if (drm_core_check_feature(dev, DRIVER_GEM)) if (drm_core_check_feature(dev, DRIVER_GEM))
...@@ -651,8 +553,49 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, ...@@ -651,8 +553,49 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
drm_fs_inode_free(dev->anon_inode); drm_fs_inode_free(dev->anon_inode);
err_free: err_free:
mutex_destroy(&dev->master_mutex); mutex_destroy(&dev->master_mutex);
kfree(dev); return ret;
return NULL; }
EXPORT_SYMBOL(drm_dev_init);
/**
* drm_dev_alloc - Allocate new DRM device
* @driver: DRM driver to allocate device for
* @parent: Parent device object
*
* Allocate and initialize a new DRM device. No device registration is done.
* Call drm_dev_register() to advertice the device to user space and register it
* with other core subsystems. This should be done last in the device
* initialization sequence to make sure userspace can't access an inconsistent
* state.
*
* The initial ref-count of the object is 1. Use drm_dev_ref() and
* drm_dev_unref() to take and drop further ref-counts.
*
* Note that for purely virtual devices @parent can be NULL.
*
* Drivers that wish to subclass or embed struct &drm_device into their
* own struct should look at using drm_dev_init() instead.
*
* RETURNS:
* Pointer to new DRM device, or NULL if out of memory.
*/
struct drm_device *drm_dev_alloc(struct drm_driver *driver,
struct device *parent)
{
struct drm_device *dev;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
ret = drm_dev_init(dev, driver, parent);
if (ret) {
kfree(dev);
return NULL;
}
return dev;
} }
EXPORT_SYMBOL(drm_dev_alloc); EXPORT_SYMBOL(drm_dev_alloc);
...@@ -716,11 +659,7 @@ EXPORT_SYMBOL(drm_dev_unref); ...@@ -716,11 +659,7 @@ EXPORT_SYMBOL(drm_dev_unref);
* *
* Register the DRM device @dev with the system, advertise device to user-space * Register the DRM device @dev with the system, advertise device to user-space
* and start normal device operation. @dev must be allocated via drm_dev_alloc() * and start normal device operation. @dev must be allocated via drm_dev_alloc()
* previously. Right after drm_dev_register() the driver should call * previously.
* drm_connector_register_all() to register all connectors in sysfs. This is
* a separate call for backward compatibility with drivers still using
* the deprecated ->load() callback, where connectors are registered from within
* the ->load() callback.
* *
* Never call this twice on any device! * Never call this twice on any device!
* *
...@@ -757,6 +696,9 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) ...@@ -757,6 +696,9 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
goto err_minors; goto err_minors;
} }
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_modeset_register_all(dev);
ret = 0; ret = 0;
goto out_unlock; goto out_unlock;
...@@ -787,6 +729,9 @@ void drm_dev_unregister(struct drm_device *dev) ...@@ -787,6 +729,9 @@ void drm_dev_unregister(struct drm_device *dev)
drm_lastclose(dev); drm_lastclose(dev);
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_modeset_unregister_all(dev);
if (dev->driver->unload) if (dev->driver->unload)
dev->driver->unload(dev); dev->driver->unload(dev);
...@@ -804,26 +749,6 @@ void drm_dev_unregister(struct drm_device *dev) ...@@ -804,26 +749,6 @@ void drm_dev_unregister(struct drm_device *dev)
} }
EXPORT_SYMBOL(drm_dev_unregister); EXPORT_SYMBOL(drm_dev_unregister);
/**
* drm_dev_set_unique - Set the unique name of a DRM device
* @dev: device of which to set the unique name
* @name: unique name
*
* Sets the unique name of a DRM device using the specified string. Drivers
* can use this at driver probe time if the unique name of the devices they
* drive is static.
*
* Return: 0 on success or a negative error code on failure.
*/
int drm_dev_set_unique(struct drm_device *dev, const char *name)
{
kfree(dev->unique);
dev->unique = kstrdup(name, GFP_KERNEL);
return dev->unique ? 0 : -ENOMEM;
}
EXPORT_SYMBOL(drm_dev_set_unique);
/* /*
* DRM Core * DRM Core
* The DRM core module initializes all global DRM objects and makes them * The DRM core module initializes all global DRM objects and makes them
......
...@@ -464,7 +464,7 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) ...@@ -464,7 +464,7 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
/* Sometimes user space wants everything disabled, so don't steal the /* Sometimes user space wants everything disabled, so don't steal the
* display if there's a master. */ * display if there's a master. */
if (dev->primary->master) if (lockless_dereference(dev->master))
return false; return false;
drm_for_each_crtc(crtc, dev) { drm_for_each_crtc(crtc, dev) {
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/module.h> #include <linux/module.h>
#include "drm_legacy.h" #include "drm_legacy.h"
#include "drm_internal.h" #include "drm_internal.h"
#include "drm_crtc_internal.h"
/* from BKL pushdown */ /* from BKL pushdown */
DEFINE_MUTEX(drm_global_mutex); DEFINE_MUTEX(drm_global_mutex);
...@@ -167,60 +168,6 @@ static int drm_cpu_valid(void) ...@@ -167,60 +168,6 @@ static int drm_cpu_valid(void)
return 1; return 1;
} }
/*
* drm_new_set_master - Allocate a new master object and become master for the
* associated master realm.
*
* @dev: The associated device.
* @fpriv: File private identifying the client.
*
* This function must be called with dev::struct_mutex held.
* Returns negative error code on failure. Zero on success.
*/
int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
{
struct drm_master *old_master;
int ret;
lockdep_assert_held_once(&dev->master_mutex);
/* create a new master */
fpriv->minor->master = drm_master_create(fpriv->minor);
if (!fpriv->minor->master)
return -ENOMEM;
/* take another reference for the copy in the local file priv */
old_master = fpriv->master;
fpriv->master = drm_master_get(fpriv->minor->master);
if (dev->driver->master_create) {
ret = dev->driver->master_create(dev, fpriv->master);
if (ret)
goto out_err;
}
if (dev->driver->master_set) {
ret = dev->driver->master_set(dev, fpriv, true);
if (ret)
goto out_err;
}
fpriv->is_master = 1;
fpriv->allowed_master = 1;
fpriv->authenticated = 1;
if (old_master)
drm_master_put(&old_master);
return 0;
out_err:
/* drop both references and restore old master on failure */
drm_master_put(&fpriv->minor->master);
drm_master_put(&fpriv->master);
fpriv->master = old_master;
return ret;
}
/* /*
* Called whenever a process opens /dev/drm. * Called whenever a process opens /dev/drm.
* *
...@@ -283,19 +230,11 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) ...@@ -283,19 +230,11 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
goto out_prime_destroy; goto out_prime_destroy;
} }
/* if there is no current master make this fd it, but do not create if (drm_is_primary_client(priv)) {
* any master object for render clients */ ret = drm_master_open(priv);
mutex_lock(&dev->master_mutex);
if (drm_is_primary_client(priv) && !priv->minor->master) {
/* create a new master */
ret = drm_new_set_master(dev, priv);
if (ret) if (ret)
goto out_close; goto out_close;
} else if (drm_is_primary_client(priv)) {
/* get a reference to the master */
priv->master = drm_master_get(priv->minor->master);
} }
mutex_unlock(&dev->master_mutex);
mutex_lock(&dev->filelist_mutex); mutex_lock(&dev->filelist_mutex);
list_add(&priv->lhead, &dev->filelist); list_add(&priv->lhead, &dev->filelist);
...@@ -324,7 +263,6 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) ...@@ -324,7 +263,6 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
return 0; return 0;
out_close: out_close:
mutex_unlock(&dev->master_mutex);
if (dev->driver->postclose) if (dev->driver->postclose)
dev->driver->postclose(dev, priv); dev->driver->postclose(dev, priv);
out_prime_destroy: out_prime_destroy:
...@@ -338,18 +276,6 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) ...@@ -338,18 +276,6 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
return ret; return ret;
} }
static void drm_master_release(struct drm_device *dev, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;
if (drm_legacy_i_have_hw_lock(dev, file_priv)) {
DRM_DEBUG("File %p released, freeing lock for context %d\n",
filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
drm_legacy_lock_free(&file_priv->master->lock,
_DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
}
}
static void drm_events_release(struct drm_file *file_priv) static void drm_events_release(struct drm_file *file_priv)
{ {
struct drm_device *dev = file_priv->minor->dev; struct drm_device *dev = file_priv->minor->dev;
...@@ -451,11 +377,6 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -451,11 +377,6 @@ int drm_release(struct inode *inode, struct file *filp)
list_del(&file_priv->lhead); list_del(&file_priv->lhead);
mutex_unlock(&dev->filelist_mutex); mutex_unlock(&dev->filelist_mutex);
mutex_lock(&dev->struct_mutex);
if (file_priv->magic)
idr_remove(&file_priv->master->magic_map, file_priv->magic);
mutex_unlock(&dev->struct_mutex);
if (dev->driver->preclose) if (dev->driver->preclose)
dev->driver->preclose(dev, file_priv); dev->driver->preclose(dev, file_priv);
...@@ -468,9 +389,8 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -468,9 +389,8 @@ int drm_release(struct inode *inode, struct file *filp)
(long)old_encode_dev(file_priv->minor->kdev->devt), (long)old_encode_dev(file_priv->minor->kdev->devt),
dev->open_count); dev->open_count);
/* if the master has gone away we can't do anything with the lock */ if (!drm_core_check_feature(dev, DRIVER_MODESET))
if (file_priv->minor->master) drm_legacy_lock_release(dev, filp);
drm_master_release(dev, filp);
if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
drm_legacy_reclaim_buffers(dev, file_priv); drm_legacy_reclaim_buffers(dev, file_priv);
...@@ -487,43 +407,12 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -487,43 +407,12 @@ int drm_release(struct inode *inode, struct file *filp)
drm_legacy_ctxbitmap_flush(dev, file_priv); drm_legacy_ctxbitmap_flush(dev, file_priv);
mutex_lock(&dev->master_mutex); if (drm_is_primary_client(file_priv))
drm_master_release(file_priv);
if (file_priv->is_master) {
struct drm_master *master = file_priv->master;
/*
* Since the master is disappearing, so is the
* possibility to lock.
*/
mutex_lock(&dev->struct_mutex);
if (master->lock.hw_lock) {
if (dev->sigdata.lock == master->lock.hw_lock)
dev->sigdata.lock = NULL;
master->lock.hw_lock = NULL;
master->lock.file_priv = NULL;
wake_up_interruptible_all(&master->lock.lock_queue);
}
mutex_unlock(&dev->struct_mutex);
if (file_priv->minor->master == file_priv->master) {
/* drop the reference held my the minor */
if (dev->driver->master_drop)
dev->driver->master_drop(dev, file_priv, true);
drm_master_put(&file_priv->minor->master);
}
}
/* drop the master reference held by the file priv */
if (file_priv->master)
drm_master_put(&file_priv->master);
file_priv->is_master = 0;
mutex_unlock(&dev->master_mutex);
if (dev->driver->postclose) if (dev->driver->postclose)
dev->driver->postclose(dev, file_priv); dev->driver->postclose(dev, file_priv);
if (drm_core_check_feature(dev, DRIVER_PRIME)) if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_prime_destroy_file_private(&file_priv->prime); drm_prime_destroy_file_private(&file_priv->prime);
......
...@@ -50,106 +50,24 @@ int drm_name_info(struct seq_file *m, void *data) ...@@ -50,106 +50,24 @@ int drm_name_info(struct seq_file *m, void *data)
struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_minor *minor = node->minor; struct drm_minor *minor = node->minor;
struct drm_device *dev = minor->dev; struct drm_device *dev = minor->dev;
struct drm_master *master = minor->master; struct drm_master *master;
if (!master)
return 0;
if (master->unique) {
seq_printf(m, "%s %s %s\n",
dev->driver->name,
dev_name(dev->dev), master->unique);
} else {
seq_printf(m, "%s %s\n",
dev->driver->name, dev_name(dev->dev));
}
return 0;
}
/**
* Called when "/proc/dri/.../vm" is read.
*
* Prints information about all mappings in drm_device::maplist.
*/
int drm_vm_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
struct drm_local_map *map;
struct drm_map_list *r_list;
/* Hardcoded from _DRM_FRAME_BUFFER,
_DRM_REGISTERS, _DRM_SHM, _DRM_AGP, and
_DRM_SCATTER_GATHER and _DRM_CONSISTENT */
const char *types[] = { "FB", "REG", "SHM", "AGP", "SG", "PCI" };
const char *type;
int i;
mutex_lock(&dev->struct_mutex);
seq_printf(m, "slot offset size type flags address mtrr\n\n");
i = 0;
list_for_each_entry(r_list, &dev->maplist, head) {
map = r_list->map;
if (!map)
continue;
if (map->type < 0 || map->type > 5)
type = "??";
else
type = types[map->type];
seq_printf(m, "%4d 0x%016llx 0x%08lx %4.4s 0x%02x 0x%08lx ",
i,
(unsigned long long)map->offset,
map->size, type, map->flags,
(unsigned long) r_list->user_token);
if (map->mtrr < 0)
seq_printf(m, "none\n");
else
seq_printf(m, "%4d\n", map->mtrr);
i++;
}
mutex_unlock(&dev->struct_mutex);
return 0;
}
/** mutex_lock(&dev->master_mutex);
* Called when "/proc/dri/.../bufs" is read. master = dev->master;
*/ if (!master)
int drm_bufs_info(struct seq_file *m, void *data) goto out_unlock;
{
struct drm_info_node *node = (struct drm_info_node *) m->private; seq_printf(m, "%s", dev->driver->name);
struct drm_device *dev = node->minor->dev; if (dev->dev)
struct drm_device_dma *dma; seq_printf(m, " dev=%s", dev_name(dev->dev));
int i, seg_pages; if (master && master->unique)
seq_printf(m, " master=%s", master->unique);
mutex_lock(&dev->struct_mutex); if (dev->unique)
dma = dev->dma; seq_printf(m, " unique=%s", dev->unique);
if (!dma) {
mutex_unlock(&dev->struct_mutex);
return 0;
}
seq_printf(m, " o size count free segs pages kB\n\n");
for (i = 0; i <= DRM_MAX_ORDER; i++) {
if (dma->bufs[i].buf_count) {
seg_pages = dma->bufs[i].seg_count * (1 << dma->bufs[i].page_order);
seq_printf(m, "%2d %8d %5d %5d %5d %5d %5ld\n",
i,
dma->bufs[i].buf_size,
dma->bufs[i].buf_count,
0,
dma->bufs[i].seg_count,
seg_pages,
seg_pages * PAGE_SIZE / 1024);
}
}
seq_printf(m, "\n");
for (i = 0; i < dma->buf_count; i++) {
if (i && !(i % 32))
seq_printf(m, "\n");
seq_printf(m, " %d", dma->buflist[i]->list);
}
seq_printf(m, "\n"); seq_printf(m, "\n");
mutex_unlock(&dev->struct_mutex); out_unlock:
mutex_unlock(&dev->master_mutex);
return 0; return 0;
} }
...@@ -184,7 +102,7 @@ int drm_clients_info(struct seq_file *m, void *data) ...@@ -184,7 +102,7 @@ int drm_clients_info(struct seq_file *m, void *data)
task ? task->comm : "<unknown>", task ? task->comm : "<unknown>",
pid_vnr(priv->pid), pid_vnr(priv->pid),
priv->minor->index, priv->minor->index,
priv->is_master ? 'y' : 'n', drm_is_current_master(priv) ? 'y' : 'n',
priv->authenticated ? 'y' : 'n', priv->authenticated ? 'y' : 'n',
from_kuid_munged(seq_user_ns(m), priv->uid), from_kuid_munged(seq_user_ns(m), priv->uid),
priv->magic); priv->magic);
...@@ -194,7 +112,6 @@ int drm_clients_info(struct seq_file *m, void *data) ...@@ -194,7 +112,6 @@ int drm_clients_info(struct seq_file *m, void *data)
return 0; return 0;
} }
static int drm_gem_one_name_info(int id, void *ptr, void *data) static int drm_gem_one_name_info(int id, void *ptr, void *data)
{ {
struct drm_gem_object *obj = ptr; struct drm_gem_object *obj = ptr;
......
...@@ -29,15 +29,9 @@ extern struct mutex drm_global_mutex; ...@@ -29,15 +29,9 @@ extern struct mutex drm_global_mutex;
void drm_lastclose(struct drm_device *dev); void drm_lastclose(struct drm_device *dev);
/* drm_pci.c */ /* drm_pci.c */
int drm_pci_set_unique(struct drm_device *dev,
struct drm_master *master,
struct drm_unique *u);
int drm_irq_by_busid(struct drm_device *dev, void *data, int drm_irq_by_busid(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
/* drm_vm.c */
int drm_vma_info(struct seq_file *m, void *data);
/* drm_prime.c */ /* drm_prime.c */
int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
...@@ -51,8 +45,6 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr ...@@ -51,8 +45,6 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr
/* drm_info.c */ /* drm_info.c */
int drm_name_info(struct seq_file *m, void *data); int drm_name_info(struct seq_file *m, void *data);
int drm_vm_info(struct seq_file *m, void *data);
int drm_bufs_info(struct seq_file *m, void *data);
int drm_clients_info(struct seq_file *m, void* data); int drm_clients_info(struct seq_file *m, void* data);
int drm_gem_name_info(struct seq_file *m, void *data); int drm_gem_name_info(struct seq_file *m, void *data);
...@@ -67,6 +59,12 @@ int drm_getmagic(struct drm_device *dev, void *data, ...@@ -67,6 +59,12 @@ int drm_getmagic(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
int drm_authmagic(struct drm_device *dev, void *data, int drm_authmagic(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
int drm_setmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_master_open(struct drm_file *file_priv);
void drm_master_release(struct drm_file *file_priv);
/* drm_sysfs.c */ /* drm_sysfs.c */
extern struct class *drm_class; extern struct class *drm_class;
...@@ -92,13 +90,6 @@ int drm_gem_open_ioctl(struct drm_device *dev, void *data, ...@@ -92,13 +90,6 @@ int drm_gem_open_ioctl(struct drm_device *dev, void *data,
void drm_gem_open(struct drm_device *dev, struct drm_file *file_private); void drm_gem_open(struct drm_device *dev, struct drm_file *file_private);
void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); void drm_gem_release(struct drm_device *dev, struct drm_file *file_private);
/* drm_drv.c */
int drm_setmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
struct drm_master *drm_master_create(struct drm_minor *minor);
/* drm_debugfs.c */ /* drm_debugfs.c */
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
int drm_debugfs_init(struct drm_minor *minor, int minor_id, int drm_debugfs_init(struct drm_minor *minor, int minor_id,
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_core.h> #include <drm/drm_core.h>
#include <drm/drm_auth.h>
#include "drm_legacy.h" #include "drm_legacy.h"
#include "drm_internal.h" #include "drm_internal.h"
#include "drm_crtc_internal.h" #include "drm_crtc_internal.h"
...@@ -37,6 +38,64 @@ ...@@ -37,6 +38,64 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/export.h> #include <linux/export.h>
/**
* DOC: getunique and setversion story
*
* BEWARE THE DRAGONS! MIND THE TRAPDOORS!
*
* In an attempt to warn anyone else who's trying to figure out what's going
* on here, I'll try to summarize the story. First things first, let's clear up
* the names, because the kernel internals, libdrm and the ioctls are all named
* differently:
*
* - GET_UNIQUE ioctl, implemented by drm_getunique is wrapped up in libdrm
* through the drmGetBusid function.
* - The libdrm drmSetBusid function is backed by the SET_UNIQUE ioctl. All
* that code is nerved in the kernel with drm_invalid_op().
* - The internal set_busid kernel functions and driver callbacks are
* exclusively use by the SET_VERSION ioctl, because only drm 1.0 (which is
* nerved) allowed userspace to set the busid through the above ioctl.
* - Other ioctls and functions involved are named consistently.
*
* For anyone wondering what's the difference between drm 1.1 and 1.4: Correctly
* handling pci domains in the busid on ppc. Doing this correctly was only
* implemented in libdrm in 2010, hence can't be nerved yet. No one knows what's
* special with drm 1.2 and 1.3.
*
* Now the actual horror story of how device lookup in drm works. At large,
* there's 2 different ways, either by busid, or by device driver name.
*
* Opening by busid is fairly simple:
*
* 1. First call SET_VERSION to make sure pci domains are handled properly. As a
* side-effect this fills out the unique name in the master structure.
* 2. Call GET_UNIQUE to read out the unique name from the master structure,
* which matches the busid thanks to step 1. If it doesn't, proceed to try
* the next device node.
*
* Opening by name is slightly different:
*
* 1. Directly call VERSION to get the version and to match against the driver
* name returned by that ioctl. Note that SET_VERSION is not called, which
* means the the unique name for the master node just opening is _not_ filled
* out. This despite that with current drm device nodes are always bound to
* one device, and can't be runtime assigned like with drm 1.0.
* 2. Match driver name. If it mismatches, proceed to the next device node.
* 3. Call GET_UNIQUE, and check whether the unique name has length zero (by
* checking that the first byte in the string is 0). If that's not the case
* libdrm skips and proceeds to the next device node. Probably this is just
* copypasta from drm 1.0 times where a set unique name meant that the driver
* was in use already, but that's just conjecture.
*
* Long story short: To keep the open by name logic working, GET_UNIQUE must
* _not_ return a unique string when SET_VERSION hasn't been called yet,
* otherwise libdrm breaks. Even when that unique string can't ever change, and
* is totally irrelevant for actually opening the device because runtime
* assignable device instances were only support in drm 1.0, which is long dead.
* But the libdrm code in drmOpenByName somehow survived, hence this can't be
* broken.
*/
static int drm_version(struct drm_device *dev, void *data, static int drm_version(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
...@@ -75,51 +134,6 @@ drm_unset_busid(struct drm_device *dev, ...@@ -75,51 +134,6 @@ drm_unset_busid(struct drm_device *dev,
master->unique_len = 0; master->unique_len = 0;
} }
/*
* Set the bus id.
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg user argument, pointing to a drm_unique structure.
* \return zero on success or a negative number on failure.
*
* Copies the bus id from userspace into drm_device::unique, and verifies that
* it matches the device this DRM is attached to (EINVAL otherwise). Deprecated
* in interface version 1.1 and will return EBUSY when setversion has requested
* version 1.1 or greater. Also note that KMS is all version 1.1 and later and
* UMS was only ever supported on pci devices.
*/
static int drm_setunique(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_unique *u = data;
struct drm_master *master = file_priv->master;
int ret;
if (master->unique_len || master->unique)
return -EBUSY;
if (!u->unique_len || u->unique_len > 1024)
return -EINVAL;
if (drm_core_check_feature(dev, DRIVER_MODESET))
return 0;
if (WARN_ON(!dev->pdev))
return -EINVAL;
ret = drm_pci_set_unique(dev, master, u);
if (ret)
goto err;
return 0;
err:
drm_unset_busid(dev, master);
return ret;
}
static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
{ {
struct drm_master *master = file_priv->master; struct drm_master *master = file_priv->master;
...@@ -135,12 +149,7 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) ...@@ -135,12 +149,7 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
return ret; return ret;
} }
} else { } else {
if (WARN(dev->unique == NULL, WARN_ON(!dev->unique);
"No drm_driver.set_busid() implementation provided by "
"%ps. Use drm_dev_set_unique() to set the unique "
"name explicitly.", dev->driver))
return -EINVAL;
master->unique = kstrdup(dev->unique, GFP_KERNEL); master->unique = kstrdup(dev->unique, GFP_KERNEL);
if (master->unique) if (master->unique)
master->unique_len = strlen(dev->unique); master->unique_len = strlen(dev->unique);
...@@ -473,7 +482,8 @@ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) ...@@ -473,7 +482,8 @@ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
return -EACCES; return -EACCES;
/* MASTER is only for master or control clients */ /* MASTER is only for master or control clients */
if (unlikely((flags & DRM_MASTER) && !file_priv->is_master && if (unlikely((flags & DRM_MASTER) &&
!drm_is_current_master(file_priv) &&
!drm_is_control_client(file_priv))) !drm_is_control_client(file_priv)))
return -EACCES; return -EACCES;
...@@ -504,7 +514,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { ...@@ -504,7 +514,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version,
DRM_UNLOCKED|DRM_RENDER_ALLOW|DRM_CONTROL_ALLOW), DRM_UNLOCKED|DRM_RENDER_ALLOW|DRM_CONTROL_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0), DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0), DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_legacy_getmap_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_legacy_getmap_ioctl, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED),
...@@ -513,10 +523,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = { ...@@ -513,10 +523,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0), DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0),
DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER),
DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_UNLOCKED|DRM_MASTER),
DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_legacy_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_legacy_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_legacy_rmmap_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_legacy_rmmap_ioctl, DRM_AUTH),
...@@ -524,8 +534,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = { ...@@ -524,8 +534,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH),
DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_UNLOCKED|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_UNLOCKED|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
......
...@@ -994,10 +994,10 @@ static void send_vblank_event(struct drm_device *dev, ...@@ -994,10 +994,10 @@ static void send_vblank_event(struct drm_device *dev,
e->event.tv_sec = now->tv_sec; e->event.tv_sec = now->tv_sec;
e->event.tv_usec = now->tv_usec; e->event.tv_usec = now->tv_usec;
drm_send_event_locked(dev, &e->base);
trace_drm_vblank_event_delivered(e->base.pid, e->pipe, trace_drm_vblank_event_delivered(e->base.pid, e->pipe,
e->event.sequence); e->event.sequence);
drm_send_event_locked(dev, &e->base);
} }
/** /**
......
...@@ -88,14 +88,10 @@ struct drm_agp_mem { ...@@ -88,14 +88,10 @@ struct drm_agp_mem {
struct list_head head; struct list_head head;
}; };
/* /* drm_lock.c */
* Generic Userspace Locking-API
*/
int drm_legacy_i_have_hw_lock(struct drm_device *d, struct drm_file *f);
int drm_legacy_lock(struct drm_device *d, void *v, struct drm_file *f); int drm_legacy_lock(struct drm_device *d, void *v, struct drm_file *f);
int drm_legacy_unlock(struct drm_device *d, void *v, struct drm_file *f); int drm_legacy_unlock(struct drm_device *d, void *v, struct drm_file *f);
int drm_legacy_lock_free(struct drm_lock_data *lock, unsigned int ctx); void drm_legacy_lock_release(struct drm_device *dev, struct file *filp);
/* DMA support */ /* DMA support */
int drm_legacy_dma_setup(struct drm_device *dev); int drm_legacy_dma_setup(struct drm_device *dev);
......
...@@ -40,6 +40,110 @@ ...@@ -40,6 +40,110 @@
static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context); static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context);
/**
* Take the heavyweight lock.
*
* \param lock lock pointer.
* \param context locking context.
* \return one if the lock is held, or zero otherwise.
*
* Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
*/
static
int drm_lock_take(struct drm_lock_data *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
spin_lock_bh(&lock_data->spinlock);
do {
old = *lock;
if (old & _DRM_LOCK_HELD)
new = old | _DRM_LOCK_CONT;
else {
new = context | _DRM_LOCK_HELD |
((lock_data->user_waiters + lock_data->kernel_waiters > 1) ?
_DRM_LOCK_CONT : 0);
}
prev = cmpxchg(lock, old, new);
} while (prev != old);
spin_unlock_bh(&lock_data->spinlock);
if (_DRM_LOCKING_CONTEXT(old) == context) {
if (old & _DRM_LOCK_HELD) {
if (context != DRM_KERNEL_CONTEXT) {
DRM_ERROR("%d holds heavyweight lock\n",
context);
}
return 0;
}
}
if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) {
/* Have lock */
return 1;
}
return 0;
}
/**
* This takes a lock forcibly and hands it to context. Should ONLY be used
* inside *_unlock to give lock to kernel before calling *_dma_schedule.
*
* \param dev DRM device.
* \param lock lock pointer.
* \param context locking context.
* \return always one.
*
* Resets the lock file pointer.
* Marks the lock as held by the given context, via the \p cmpxchg instruction.
*/
static int drm_lock_transfer(struct drm_lock_data *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
lock_data->file_priv = NULL;
do {
old = *lock;
new = context | _DRM_LOCK_HELD;
prev = cmpxchg(lock, old, new);
} while (prev != old);
return 1;
}
static int drm_legacy_lock_free(struct drm_lock_data *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
spin_lock_bh(&lock_data->spinlock);
if (lock_data->kernel_waiters != 0) {
drm_lock_transfer(lock_data, 0);
lock_data->idle_has_lock = 1;
spin_unlock_bh(&lock_data->spinlock);
return 1;
}
spin_unlock_bh(&lock_data->spinlock);
do {
old = *lock;
new = _DRM_LOCKING_CONTEXT(old);
prev = cmpxchg(lock, old, new);
} while (prev != old);
if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
DRM_ERROR("%d freed heavyweight lock held by %d\n",
context, _DRM_LOCKING_CONTEXT(old));
return 1;
}
wake_up_interruptible(&lock_data->lock_queue);
return 0;
}
/** /**
* Lock ioctl. * Lock ioctl.
* *
...@@ -115,7 +219,7 @@ int drm_legacy_lock(struct drm_device *dev, void *data, ...@@ -115,7 +219,7 @@ int drm_legacy_lock(struct drm_device *dev, void *data,
/* don't set the block all signals on the master process for now /* don't set the block all signals on the master process for now
* really probably not the correct answer but lets us debug xkb * really probably not the correct answer but lets us debug xkb
* xserver for now */ * xserver for now */
if (!file_priv->is_master) { if (!drm_is_current_master(file_priv)) {
dev->sigdata.context = lock->context; dev->sigdata.context = lock->context;
dev->sigdata.lock = master->lock.hw_lock; dev->sigdata.lock = master->lock.hw_lock;
} }
...@@ -164,120 +268,6 @@ int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_ ...@@ -164,120 +268,6 @@ int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_
return 0; return 0;
} }
/**
* Take the heavyweight lock.
*
* \param lock lock pointer.
* \param context locking context.
* \return one if the lock is held, or zero otherwise.
*
* Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
*/
static
int drm_lock_take(struct drm_lock_data *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
spin_lock_bh(&lock_data->spinlock);
do {
old = *lock;
if (old & _DRM_LOCK_HELD)
new = old | _DRM_LOCK_CONT;
else {
new = context | _DRM_LOCK_HELD |
((lock_data->user_waiters + lock_data->kernel_waiters > 1) ?
_DRM_LOCK_CONT : 0);
}
prev = cmpxchg(lock, old, new);
} while (prev != old);
spin_unlock_bh(&lock_data->spinlock);
if (_DRM_LOCKING_CONTEXT(old) == context) {
if (old & _DRM_LOCK_HELD) {
if (context != DRM_KERNEL_CONTEXT) {
DRM_ERROR("%d holds heavyweight lock\n",
context);
}
return 0;
}
}
if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) {
/* Have lock */
return 1;
}
return 0;
}
/**
* This takes a lock forcibly and hands it to context. Should ONLY be used
* inside *_unlock to give lock to kernel before calling *_dma_schedule.
*
* \param dev DRM device.
* \param lock lock pointer.
* \param context locking context.
* \return always one.
*
* Resets the lock file pointer.
* Marks the lock as held by the given context, via the \p cmpxchg instruction.
*/
static int drm_lock_transfer(struct drm_lock_data *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
lock_data->file_priv = NULL;
do {
old = *lock;
new = context | _DRM_LOCK_HELD;
prev = cmpxchg(lock, old, new);
} while (prev != old);
return 1;
}
/**
* Free lock.
*
* \param dev DRM device.
* \param lock lock.
* \param context context.
*
* Resets the lock file pointer.
* Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task
* waiting on the lock queue.
*/
int drm_legacy_lock_free(struct drm_lock_data *lock_data, unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
spin_lock_bh(&lock_data->spinlock);
if (lock_data->kernel_waiters != 0) {
drm_lock_transfer(lock_data, 0);
lock_data->idle_has_lock = 1;
spin_unlock_bh(&lock_data->spinlock);
return 1;
}
spin_unlock_bh(&lock_data->spinlock);
do {
old = *lock;
new = _DRM_LOCKING_CONTEXT(old);
prev = cmpxchg(lock, old, new);
} while (prev != old);
if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
DRM_ERROR("%d freed heavyweight lock held by %d\n",
context, _DRM_LOCKING_CONTEXT(old));
return 1;
}
wake_up_interruptible(&lock_data->lock_queue);
return 0;
}
/** /**
* This function returns immediately and takes the hw lock * This function returns immediately and takes the hw lock
* with the kernel context if it is free, otherwise it gets the highest priority when and if * with the kernel context if it is free, otherwise it gets the highest priority when and if
...@@ -330,11 +320,27 @@ void drm_legacy_idlelock_release(struct drm_lock_data *lock_data) ...@@ -330,11 +320,27 @@ void drm_legacy_idlelock_release(struct drm_lock_data *lock_data)
} }
EXPORT_SYMBOL(drm_legacy_idlelock_release); EXPORT_SYMBOL(drm_legacy_idlelock_release);
int drm_legacy_i_have_hw_lock(struct drm_device *dev, static int drm_legacy_i_have_hw_lock(struct drm_device *dev,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
struct drm_master *master = file_priv->master; struct drm_master *master = file_priv->master;
return (file_priv->lock_count && master->lock.hw_lock && return (file_priv->lock_count && master->lock.hw_lock &&
_DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) && _DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) &&
master->lock.file_priv == file_priv); master->lock.file_priv == file_priv);
} }
void drm_legacy_lock_release(struct drm_device *dev, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;
/* if the master has gone away we can't do anything with the lock */
if (!dev->master)
return;
if (drm_legacy_i_have_hw_lock(dev, file_priv)) {
DRM_DEBUG("File %p released, freeing lock for context %d\n",
filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
drm_legacy_lock_free(&file_priv->master->lock,
_DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
}
}
...@@ -144,50 +144,6 @@ int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) ...@@ -144,50 +144,6 @@ int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
} }
EXPORT_SYMBOL(drm_pci_set_busid); EXPORT_SYMBOL(drm_pci_set_busid);
int drm_pci_set_unique(struct drm_device *dev,
struct drm_master *master,
struct drm_unique *u)
{
int domain, bus, slot, func, ret;
master->unique_len = u->unique_len;
master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
if (!master->unique) {
ret = -ENOMEM;
goto err;
}
if (copy_from_user(master->unique, u->unique, master->unique_len)) {
ret = -EFAULT;
goto err;
}
master->unique[master->unique_len] = '\0';
/* Return error if the busid submitted doesn't match the device's actual
* busid.
*/
ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
if (ret != 3) {
ret = -EINVAL;
goto err;
}
domain = bus >> 8;
bus &= 0xff;
if ((domain != drm_get_pci_domain(dev)) ||
(bus != dev->pdev->bus->number) ||
(slot != PCI_SLOT(dev->pdev->devfn)) ||
(func != PCI_FUNC(dev->pdev->devfn))) {
ret = -EINVAL;
goto err;
}
return 0;
err:
return ret;
}
static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p) static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
{ {
if ((p->busnum >> 8) != drm_get_pci_domain(dev) || if ((p->busnum >> 8) != drm_get_pci_domain(dev) ||
...@@ -444,13 +400,6 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, ...@@ -444,13 +400,6 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
{ {
return -EINVAL; return -EINVAL;
} }
int drm_pci_set_unique(struct drm_device *dev,
struct drm_master *master,
struct drm_unique *u)
{
return -EINVAL;
}
#endif #endif
EXPORT_SYMBOL(drm_pci_init); EXPORT_SYMBOL(drm_pci_init);
......
...@@ -115,6 +115,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, ...@@ -115,6 +115,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
* @src: source coordinates in 16.16 fixed point * @src: source coordinates in 16.16 fixed point
* @dest: integer destination coordinates * @dest: integer destination coordinates
* @clip: integer clipping coordinates * @clip: integer clipping coordinates
* @rotation: plane rotation
* @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
* @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
* @can_position: is it legal to position the plane such that it * @can_position: is it legal to position the plane such that it
...@@ -134,16 +135,17 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, ...@@ -134,16 +135,17 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
* Zero if update appears valid, error code on failure * Zero if update appears valid, error code on failure
*/ */
int drm_plane_helper_check_update(struct drm_plane *plane, int drm_plane_helper_check_update(struct drm_plane *plane,
struct drm_crtc *crtc, struct drm_crtc *crtc,
struct drm_framebuffer *fb, struct drm_framebuffer *fb,
struct drm_rect *src, struct drm_rect *src,
struct drm_rect *dest, struct drm_rect *dest,
const struct drm_rect *clip, const struct drm_rect *clip,
int min_scale, unsigned int rotation,
int max_scale, int min_scale,
bool can_position, int max_scale,
bool can_update_disabled, bool can_position,
bool *visible) bool can_update_disabled,
bool *visible)
{ {
int hscale, vscale; int hscale, vscale;
...@@ -163,6 +165,8 @@ int drm_plane_helper_check_update(struct drm_plane *plane, ...@@ -163,6 +165,8 @@ int drm_plane_helper_check_update(struct drm_plane *plane,
return -EINVAL; return -EINVAL;
} }
drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation);
/* Check scaling */ /* Check scaling */
hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale); hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale);
vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale); vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale);
...@@ -174,6 +178,9 @@ int drm_plane_helper_check_update(struct drm_plane *plane, ...@@ -174,6 +178,9 @@ int drm_plane_helper_check_update(struct drm_plane *plane,
} }
*visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale); *visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale);
drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation);
if (!*visible) if (!*visible)
/* /*
* Plane isn't visible; some drivers can handle this * Plane isn't visible; some drivers can handle this
...@@ -267,6 +274,7 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, ...@@ -267,6 +274,7 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
ret = drm_plane_helper_check_update(plane, crtc, fb, ret = drm_plane_helper_check_update(plane, crtc, fb,
&src, &dest, &clip, &src, &dest, &clip,
BIT(DRM_ROTATE_0),
DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING,
false, false, &visible); false, false, &visible);
......
...@@ -68,24 +68,6 @@ static int drm_get_platform_dev(struct platform_device *platdev, ...@@ -68,24 +68,6 @@ static int drm_get_platform_dev(struct platform_device *platdev,
return ret; return ret;
} }
int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master)
{
int id;
id = dev->platformdev->id;
if (id < 0)
id = 0;
master->unique = kasprintf(GFP_KERNEL, "platform:%s:%02d",
dev->platformdev->name, id);
if (!master->unique)
return -ENOMEM;
master->unique_len = strlen(master->unique);
return 0;
}
EXPORT_SYMBOL(drm_platform_set_busid);
/** /**
* drm_platform_init - Register a platform device with the DRM subsystem * drm_platform_init - Register a platform device with the DRM subsystem
* @driver: DRM device driver * @driver: DRM device driver
......
...@@ -105,6 +105,7 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, ...@@ -105,6 +105,7 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
ret = drm_plane_helper_check_update(plane, &pipe->crtc, ret = drm_plane_helper_check_update(plane, &pipe->crtc,
plane_state->fb, plane_state->fb,
&src, &dest, &clip, &src, &dest, &clip,
plane_state->rotation,
DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING,
false, true, &visible); false, true, &visible);
......
...@@ -670,57 +670,3 @@ void drm_legacy_vma_flush(struct drm_device *dev) ...@@ -670,57 +670,3 @@ void drm_legacy_vma_flush(struct drm_device *dev)
kfree(vma); kfree(vma);
} }
} }
int drm_vma_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
struct drm_vma_entry *pt;
struct vm_area_struct *vma;
unsigned long vma_count = 0;
#if defined(__i386__)
unsigned int pgprot;
#endif
mutex_lock(&dev->struct_mutex);
list_for_each_entry(pt, &dev->vmalist, head)
vma_count++;
seq_printf(m, "vma use count: %lu, high_memory = %pK, 0x%pK\n",
vma_count, high_memory,
(void *)(unsigned long)virt_to_phys(high_memory));
list_for_each_entry(pt, &dev->vmalist, head) {
vma = pt->vma;
if (!vma)
continue;
seq_printf(m,
"\n%5d 0x%pK-0x%pK %c%c%c%c%c%c 0x%08lx000",
pt->pid,
(void *)vma->vm_start, (void *)vma->vm_end,
vma->vm_flags & VM_READ ? 'r' : '-',
vma->vm_flags & VM_WRITE ? 'w' : '-',
vma->vm_flags & VM_EXEC ? 'x' : '-',
vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
vma->vm_flags & VM_LOCKED ? 'l' : '-',
vma->vm_flags & VM_IO ? 'i' : '-',
vma->vm_pgoff);
#if defined(__i386__)
pgprot = pgprot_val(vma->vm_page_prot);
seq_printf(m, " %c%c%c%c%c%c%c%c%c",
pgprot & _PAGE_PRESENT ? 'p' : '-',
pgprot & _PAGE_RW ? 'w' : 'r',
pgprot & _PAGE_USER ? 'u' : 's',
pgprot & _PAGE_PWT ? 't' : 'b',
pgprot & _PAGE_PCD ? 'u' : 'c',
pgprot & _PAGE_ACCESSED ? 'a' : '-',
pgprot & _PAGE_DIRTY ? 'd' : '-',
pgprot & _PAGE_PSE ? 'm' : 'k',
pgprot & _PAGE_GLOBAL ? 'g' : 'l');
#endif
seq_printf(m, "\n");
}
mutex_unlock(&dev->struct_mutex);
return 0;
}
...@@ -496,7 +496,6 @@ static struct drm_driver etnaviv_drm_driver = { ...@@ -496,7 +496,6 @@ static struct drm_driver etnaviv_drm_driver = {
DRIVER_RENDER, DRIVER_RENDER,
.open = etnaviv_open, .open = etnaviv_open,
.preclose = etnaviv_preclose, .preclose = etnaviv_preclose,
.set_busid = drm_platform_set_busid,
.gem_free_object_unlocked = etnaviv_gem_free_object, .gem_free_object_unlocked = etnaviv_gem_free_object,
.gem_vm_ops = &vm_ops, .gem_vm_ops = &vm_ops,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
......
...@@ -407,7 +407,6 @@ static struct drm_driver exynos_drm_driver = { ...@@ -407,7 +407,6 @@ static struct drm_driver exynos_drm_driver = {
.preclose = exynos_drm_preclose, .preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose, .lastclose = exynos_drm_lastclose,
.postclose = exynos_drm_postclose, .postclose = exynos_drm_postclose,
.set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_no_hw_counter, .get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = exynos_drm_crtc_enable_vblank, .enable_vblank = exynos_drm_crtc_enable_vblank,
.disable_vblank = exynos_drm_crtc_disable_vblank, .disable_vblank = exynos_drm_crtc_disable_vblank,
......
...@@ -37,23 +37,22 @@ int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev) ...@@ -37,23 +37,22 @@ int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev)
ret = fsl_dcu_drm_crtc_create(fsl_dev); ret = fsl_dcu_drm_crtc_create(fsl_dev);
if (ret) if (ret)
return ret; goto err;
ret = fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc); ret = fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc);
if (ret) if (ret)
goto fail_encoder; goto err;
ret = fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder); ret = fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder);
if (ret) if (ret)
goto fail_connector; goto err;
drm_mode_config_reset(fsl_dev->drm); drm_mode_config_reset(fsl_dev->drm);
drm_kms_helper_poll_init(fsl_dev->drm); drm_kms_helper_poll_init(fsl_dev->drm);
return 0; return 0;
fail_encoder:
fsl_dev->crtc.funcs->destroy(&fsl_dev->crtc); err:
fail_connector: drm_mode_config_cleanup(fsl_dev->drm);
fsl_dev->encoder.funcs->destroy(&fsl_dev->encoder);
return ret; return ret;
} }
...@@ -171,7 +171,6 @@ static struct drm_driver kirin_drm_driver = { ...@@ -171,7 +171,6 @@ static struct drm_driver kirin_drm_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC | DRIVER_HAVE_IRQ, DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
.fops = &kirin_drm_fops, .fops = &kirin_drm_fops,
.set_busid = drm_platform_set_busid,
.gem_free_object_unlocked = drm_gem_cma_free_object, .gem_free_object_unlocked = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops, .gem_vm_ops = &drm_gem_cma_vm_ops,
...@@ -221,19 +220,12 @@ static int kirin_drm_bind(struct device *dev) ...@@ -221,19 +220,12 @@ static int kirin_drm_bind(struct device *dev)
if (ret) if (ret)
goto err_kms_cleanup; goto err_kms_cleanup;
/* connectors should be registered after drm device register */
ret = drm_connector_register_all(drm_dev);
if (ret)
goto err_drm_dev_unregister;
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
driver->name, driver->major, driver->minor, driver->patchlevel, driver->name, driver->major, driver->minor, driver->patchlevel,
driver->date, drm_dev->primary->index); driver->date, drm_dev->primary->index);
return 0; return 0;
err_drm_dev_unregister:
drm_dev_unregister(drm_dev);
err_kms_cleanup: err_kms_cleanup:
kirin_drm_kms_cleanup(drm_dev); kirin_drm_kms_cleanup(drm_dev);
err_drm_dev_unref: err_drm_dev_unref:
...@@ -246,7 +238,6 @@ static void kirin_drm_unbind(struct device *dev) ...@@ -246,7 +238,6 @@ static void kirin_drm_unbind(struct device *dev)
{ {
struct drm_device *drm_dev = dev_get_drvdata(dev); struct drm_device *drm_dev = dev_get_drvdata(dev);
drm_connector_unregister_all(drm_dev);
drm_dev_unregister(drm_dev); drm_dev_unregister(drm_dev);
kirin_drm_kms_cleanup(drm_dev); kirin_drm_kms_cleanup(drm_dev);
drm_dev_unref(drm_dev); drm_dev_unref(drm_dev);
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include <drm/intel-gtt.h> #include <drm/intel-gtt.h>
#include <drm/drm_legacy.h> /* for struct drm_dma_handle */ #include <drm/drm_legacy.h> /* for struct drm_dma_handle */
#include <drm/drm_gem.h> #include <drm/drm_gem.h>
#include <drm/drm_auth.h>
#include "i915_params.h" #include "i915_params.h"
#include "i915_reg.h" #include "i915_reg.h"
...@@ -3673,7 +3674,7 @@ extern void intel_modeset_init_hw(struct drm_device *dev); ...@@ -3673,7 +3674,7 @@ extern void intel_modeset_init_hw(struct drm_device *dev);
extern void intel_modeset_init(struct drm_device *dev); extern void intel_modeset_init(struct drm_device *dev);
extern void intel_modeset_gem_init(struct drm_device *dev); extern void intel_modeset_gem_init(struct drm_device *dev);
extern void intel_modeset_cleanup(struct drm_device *dev); extern void intel_modeset_cleanup(struct drm_device *dev);
extern void intel_connector_unregister(struct intel_connector *); extern void intel_connector_unregister(struct drm_connector *);
extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
extern void intel_display_resume(struct drm_device *dev); extern void intel_display_resume(struct drm_device *dev);
extern void i915_redisable_vga(struct drm_device *dev); extern void i915_redisable_vga(struct drm_device *dev);
......
...@@ -1446,7 +1446,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, ...@@ -1446,7 +1446,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
dispatch_flags = 0; dispatch_flags = 0;
if (args->flags & I915_EXEC_SECURE) { if (args->flags & I915_EXEC_SECURE) {
if (!file->is_master || !capable(CAP_SYS_ADMIN)) if (!drm_is_current_master(file) || !capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
dispatch_flags |= I915_DISPATCH_SECURE; dispatch_flags |= I915_DISPATCH_SECURE;
...@@ -1553,7 +1553,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, ...@@ -1553,7 +1553,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
batch_obj, batch_obj,
args->batch_start_offset, args->batch_start_offset,
args->batch_len, args->batch_len,
file->is_master); drm_is_current_master(file));
if (IS_ERR(parsed_batch_obj)) { if (IS_ERR(parsed_batch_obj)) {
ret = PTR_ERR(parsed_batch_obj); ret = PTR_ERR(parsed_batch_obj);
goto err; goto err;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -407,7 +407,6 @@ static struct drm_driver imx_drm_driver = { ...@@ -407,7 +407,6 @@ static struct drm_driver imx_drm_driver = {
.load = imx_drm_driver_load, .load = imx_drm_driver_load,
.unload = imx_drm_driver_unload, .unload = imx_drm_driver_unload,
.lastclose = imx_drm_driver_lastclose, .lastclose = imx_drm_driver_lastclose,
.set_busid = drm_platform_set_busid,
.gem_free_object_unlocked = drm_gem_cma_free_object, .gem_free_object_unlocked = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops, .gem_vm_ops = &drm_gem_cma_vm_ops,
.dumb_create = drm_gem_cma_dumb_create, .dumb_create = drm_gem_cma_dumb_create,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment