drm-misc-next for v6.9:

UAPI Changes:
 
 virtio:
 - add Venus capset defines
 
 Cross-subsystem Changes:
 
 Core Changes:
 
 - fix drm_fixp2int_ceil()
 - documentation fixes
 - clean ups
 - allow DRM_MM_DEBUG with DRM=m
 - build fixes for debugfs support
 - EDID cleanups
 - sched: error-handling fixes
 - ttm: add tests
 
 Driver Changes:
 
 bridge:
 - ite-6505: fix DP link-training bug
 - samsung-dsim: fix error checking in probe
 - tc358767: fix regmap usage
 
 efifb:
 - use copy of global screen_info state
 
 hisilicon:
 - fix EDID includes
 
 mgag200:
 - improve ioremap usage
 - convert to struct drm_edid
 
 nouveau:
 - disp: use kmemdup()
 - fix EDID includes
 - documentation fixes
 
 panel:
 - ltk050h3146w: error-handling fixes
 - panel-edp: support delay between power-on and enable; use put_sync in
   unprepare; support Mediatek MT8173 Chromebooks, BOE NV116WHM-N49 V8.0,
   BOE NV122WUM-N41, CSO MNC207QS1-1 plus DT bindings
 - panel-lvds: support EDT ETML0700Z9NDHA plus DT bindings
 - panel-novatek: FRIDA FRD400B25025-A-CTK plus DT bindings
 
 qaic:
 - fixes to BO handling
 - make use of DRM managed release
 - fix order of remove operations
 
 rockchip:
 - analogix_dp: get encoder port from DT
 - inno_hdmi: support HDMI for RK3128
 - lvds: error-handling fixes
 
 simplefb:
 - fix logging
 
 ssd130x:
 - support SSD133x plus DT bindings
 
 tegra:
 - fix error handling
 
 tilcdc:
 - make use of DRM managed release
 
 v3d:
 - show memory stats in debugfs
 
 vc4:
 - fix error handling in plane prepare_fb
 - fix framebuffer test in plane helpers
 
 vesafb:
 - use copy of global screen_info state
 
 virtio:
 - cleanups
 
 vkms:
 - fix OOB access when programming the LUT
 - Kconfig improvements
 
 vmwgfx:
 - unmap surface before changing plane state
 - fix memory leak in error handling
 - documentation fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmWgDSsACgkQaA3BHVML
 eiPG5AgAjK42pY25OezV/wmUiffhfDluTbjw+Cjwg/IzZ+RyAMcf1q7R8Oid6G6o
 kG64WfUmBURwdOQ2nvtJGum8YoBx70uvmKyFzhu20v+e9HT/EFizRi7Qvg71Il/0
 l8WQ5BJ1W6PvJKgEU4io+OWWhDyZ1B3GpCvkd30wF2Vfi0CPeeVAvwmeKC5Rj4lE
 lbYa93sAS/neNkTBvhEEUGoz/t9+5FqAMarygM6hYzDVgnhs8Mbm/pRKCUgPvYvp
 871oibBPenQGP6jkWU1h1UWIa5CACvHgkebSGUGy6b5e9jPBRrSmlnRhiviJEY1/
 6x7kGnB69r6I40CdY/1cnTqyhJ25cA==
 =GX2h
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2024-01-11' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for v6.9:

UAPI Changes:

virtio:
- add Venus capset defines

Cross-subsystem Changes:

Core Changes:

- fix drm_fixp2int_ceil()
- documentation fixes
- clean ups
- allow DRM_MM_DEBUG with DRM=m
- build fixes for debugfs support
- EDID cleanups
- sched: error-handling fixes
- ttm: add tests

Driver Changes:

bridge:
- ite-6505: fix DP link-training bug
- samsung-dsim: fix error checking in probe
- tc358767: fix regmap usage

efifb:
- use copy of global screen_info state

hisilicon:
- fix EDID includes

mgag200:
- improve ioremap usage
- convert to struct drm_edid

nouveau:
- disp: use kmemdup()
- fix EDID includes
- documentation fixes

panel:
- ltk050h3146w: error-handling fixes
- panel-edp: support delay between power-on and enable; use put_sync in
  unprepare; support Mediatek MT8173 Chromebooks, BOE NV116WHM-N49 V8.0,
  BOE NV122WUM-N41, CSO MNC207QS1-1 plus DT bindings
- panel-lvds: support EDT ETML0700Z9NDHA plus DT bindings
- panel-novatek: FRIDA FRD400B25025-A-CTK plus DT bindings

qaic:
- fixes to BO handling
- make use of DRM managed release
- fix order of remove operations

rockchip:
- analogix_dp: get encoder port from DT
- inno_hdmi: support HDMI for RK3128
- lvds: error-handling fixes

simplefb:
- fix logging

ssd130x:
- support SSD133x plus DT bindings

tegra:
- fix error handling

tilcdc:
- make use of DRM managed release

v3d:
- show memory stats in debugfs

vc4:
- fix error handling in plane prepare_fb
- fix framebuffer test in plane helpers

vesafb:
- use copy of global screen_info state

virtio:
- cleanups

vkms:
- fix OOB access when programming the LUT
- Kconfig improvements

vmwgfx:
- unmap surface before changing plane state
- fix memory leak in error handling
- documentation fixes

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20240111154902.GA8448@linux-uq9g
This commit is contained in:
Dave Airlie 2024-02-05 13:49:47 +10:00
commit f8e4806e0d
84 changed files with 3411 additions and 1111 deletions

View File

@ -15,7 +15,9 @@ allOf:
properties:
compatible:
items:
- const: hydis,hva40wv1
- enum:
- frida,frd400b25025
- hydis,hva40wv1
- const: novatek,nt35510
description: This indicates the panel manufacturer of the panel
that is in turn using the NT35510 panel driver. The compatible

View File

@ -42,6 +42,8 @@ properties:
- auo,b101ew05
# Chunghwa Picture Tubes Ltd. 7" WXGA (800x1280) TFT LCD LVDS panel
- chunghwa,claa070wp03xg
# EDT ETML0700Z9NDHA 7.0" WSVGA (1024x600) color TFT LCD LVDS panel
- edt,etml0700z9ndha
# HannStar Display Corp. HSD101PWW2 10.1" WXGA (1280x800) LVDS panel
- hannstar,hsd101pww2
# Hydis Technologies 7" WXGA (800x1280) TFT LCD LVDS panel

View File

@ -131,9 +131,9 @@ allOf:
const: sinowealth,sh1106
then:
properties:
width:
solomon,width:
default: 132
height:
solomon,height:
default: 64
solomon,dclk-div:
default: 1
@ -149,9 +149,9 @@ allOf:
- solomon,ssd1305
then:
properties:
width:
solomon,width:
default: 132
height:
solomon,height:
default: 64
solomon,dclk-div:
default: 1
@ -167,9 +167,9 @@ allOf:
- solomon,ssd1306
then:
properties:
width:
solomon,width:
default: 128
height:
solomon,height:
default: 64
solomon,dclk-div:
default: 1
@ -185,9 +185,9 @@ allOf:
- solomon,ssd1307
then:
properties:
width:
solomon,width:
default: 128
height:
solomon,height:
default: 39
solomon,dclk-div:
default: 2
@ -205,9 +205,9 @@ allOf:
- solomon,ssd1309
then:
properties:
width:
solomon,width:
default: 128
height:
solomon,height:
default: 64
solomon,dclk-div:
default: 1

View File

@ -30,9 +30,9 @@ allOf:
const: solomon,ssd1322
then:
properties:
width:
solomon,width:
default: 480
height:
solomon,height:
default: 128
- if:
@ -42,9 +42,9 @@ allOf:
const: solomon,ssd1325
then:
properties:
width:
solomon,width:
default: 128
height:
solomon,height:
default: 80
- if:
@ -54,9 +54,9 @@ allOf:
const: solomon,ssd1327
then:
properties:
width:
solomon,width:
default: 128
height:
solomon,height:
default: 128
unevaluatedProperties: false

View File

@ -0,0 +1,45 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/solomon,ssd133x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Solomon SSD133x OLED Display Controllers
maintainers:
- Javier Martinez Canillas <javierm@redhat.com>
allOf:
- $ref: solomon,ssd-common.yaml#
properties:
compatible:
enum:
- solomon,ssd1331
solomon,width:
default: 96
solomon,height:
default: 64
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
oled@0 {
compatible = "solomon,ssd1331";
reg = <0x0>;
reset-gpios = <&gpio2 7>;
dc-gpios = <&gpio2 8>;
spi-max-frequency = <10000000>;
};
};

View File

@ -1,234 +0,0 @@
==========================
Xe Merge Acceptance Plan
==========================
Xe is a new driver for Intel GPUs that supports both integrated and
discrete platforms starting with Tiger Lake (first Intel Xe Architecture).
This document aims to establish a merge plan for the Xe, by writing down clear
pre-merge goals, in order to avoid unnecessary delays.
Xe Overview
=============
The main motivation of Xe is to have a fresh base to work from that is
unencumbered by older platforms, whilst also taking the opportunity to
rearchitect our driver to increase sharing across the drm subsystem, both
leveraging and allowing us to contribute more towards other shared components
like TTM and drm/scheduler.
This is also an opportunity to start from the beginning with a clean uAPI that is
extensible by design and already aligned with the modern userspace needs. For
this reason, the memory model is solely based on GPU Virtual Address space
bind/unbind (VM_BIND) of GEM buffer objects (BOs) and execution only supporting
explicit synchronization. With persistent mapping across the execution, the
userspace does not need to provide a list of all required mappings during each
submission.
The new driver leverages a lot from i915. As for display, the intent is to share
the display code with the i915 driver so that there is maximum reuse there.
As for the power management area, the goal is to have a much-simplified support
for the system suspend states (S-states), PCI device suspend states (D-states),
GPU/Render suspend states (R-states) and frequency management. It should leverage
as much as possible all the existent PCI-subsystem infrastructure (pm and
runtime_pm) and underlying firmware components such PCODE and GuC for the power
states and frequency decisions.
Repository:
https://gitlab.freedesktop.org/drm/xe/kernel (branch drm-xe-next)
Xe Platforms
==============
Currently, Xe is already functional and has experimental support for multiple
platforms starting from Tiger Lake, with initial support in userspace implemented
in Mesa (for Iris and Anv, our OpenGL and Vulkan drivers), as well as in NEO
(for OpenCL and Level0).
During a transition period, platforms will be supported by both Xe and i915.
However, the force_probe mechanism existent in both drivers will allow only one
official and by-default probe at a given time.
For instance, in order to probe a DG2 which PCI ID is 0x5690 by Xe instead of
i915, the following set of parameters need to be used:
```
i915.force_probe=!5690 xe.force_probe=5690
```
In both drivers, the .require_force_probe protection forces the user to use the
force_probe parameter while the driver is under development. This protection is
only removed when the support for the platform and the uAPI are stable. Stability
which needs to be demonstrated by CI results.
In order to avoid user space regressions, i915 will continue to support all the
current platforms that are already out of this protection. Xe support will be
forever experimental and dependent on the usage of force_probe for these
platforms.
When the time comes for Xe, the protection will be lifted on Xe and kept in i915.
Xe Pre-Merge Goals - Work-in-Progress
=======================================
Display integration with i915
-----------------------------
In order to share the display code with the i915 driver so that there is maximum
reuse, the i915/display/ code is built twice, once for i915.ko and then for
xe.ko. Currently, the i915/display code in Xe tree is polluted with many 'ifdefs'
depending on the build target. The goal is to refactor both Xe and i915/display
code simultaneously in order to get a clean result before they land upstream, so
that display can already be part of the initial pull request towards drm-next.
However, display code should not gate the acceptance of Xe in upstream. Xe
patches will be refactored in a way that display code can be removed, if needed,
from the first pull request of Xe towards drm-next. The expectation is that when
both drivers are part of the drm-tip, the introduction of cleaner patches will be
easier and speed up.
Xe uAPI high level overview
=============================
...Warning: To be done in follow up patches after/when/where the main consensus in various items are individually reached.
Xe Pre-Merge Goals - Completed
================================
Drm_exec
--------
Helper to make dma_resv locking for a big number of buffers is getting removed in
the drm_exec series proposed in https://patchwork.freedesktop.org/patch/524376/
If that happens, Xe needs to change and incorporate the changes in the driver.
The goal is to engage with the Community to understand if the best approach is to
move that to the drivers that are using it or if we should keep the helpers in
place waiting for Xe to get merged.
This item ties into the GPUVA, VM_BIND, and even long-running compute support.
As a key measurable result, we need to have a community consensus documented in
this document and the Xe driver prepared for the changes, if necessary.
Userptr integration and vm_bind
-------------------------------
Different drivers implement different ways of dealing with execution of userptr.
With multiple drivers currently introducing support to VM_BIND, the goal is to
aim for a DRM consensus on whats the best way to have that support. To some
extent this is already getting addressed itself with the GPUVA where likely the
userptr will be a GPUVA with a NULL GEM call VM bind directly on the userptr.
However, there are more aspects around the rules for that and the usage of
mmu_notifiers, locking and other aspects.
This task here has the goal of introducing a documentation of the basic rules.
The documentation *needs* to first live in this document (API session below) and
then moved to another more specific document or at Xe level or at DRM level.
Documentation should include:
* The userptr part of the VM_BIND api.
* Locking, including the page-faulting case.
* O(1) complexity under VM_BIND.
The document is now included in the drm documentation :doc:`here </gpu/drm-vm-bind-async>`.
Some parts of userptr like mmu_notifiers should become GPUVA or DRM helpers when
the second driver supporting VM_BIND+userptr appears. Details to be defined when
the time comes.
The DRM GPUVM helpers do not yet include the userptr parts, but discussions
about implementing them are ongoing.
ASYNC VM_BIND
-------------
Although having a common DRM level IOCTL for VM_BIND is not a requirement to get
Xe merged, it is mandatory to have a consensus with other drivers and Mesa.
It needs to be clear how to handle async VM_BIND and interactions with userspace
memory fences. Ideally with helper support so people don't get it wrong in all
possible ways.
As a key measurable result, the benefits of ASYNC VM_BIND and a discussion of
various flavors, error handling and sample API suggestions are documented in
:doc:`The ASYNC VM_BIND document </gpu/drm-vm-bind-async>`.
Drm_scheduler
-------------
Xe primarily uses Firmware based scheduling (GuC FW). However, it will use
drm_scheduler as the scheduler frontend for userspace submission in order to
resolve syncobj and dma-buf implicit sync dependencies. However, drm_scheduler is
not yet prepared to handle the 1-to-1 relationship between drm_gpu_scheduler and
drm_sched_entity.
Deeper changes to drm_scheduler should *not* be required to get Xe accepted, but
some consensus needs to be reached between Xe and other community drivers that
could also benefit from this work, for coupling FW based/assisted submission such
as the ARMs new Mali GPU driver, and others.
As a key measurable result, the patch series introducing Xe itself shall not
depend on any other patch touching drm_scheduler itself that was not yet merged
through drm-misc. This, by itself, already includes the reach of an agreement for
uniform 1 to 1 relationship implementation / usage across drivers.
Long running compute: minimal data structure/scaffolding
--------------------------------------------------------
The generic scheduler code needs to include the handling of endless compute
contexts, with the minimal scaffolding for preempt-ctx fences (probably on the
drm_sched_entity) and making sure drm_scheduler can cope with the lack of job
completion fence.
The goal is to achieve a consensus ahead of Xe initial pull-request, ideally with
this minimal drm/scheduler work, if needed, merged to drm-misc in a way that any
drm driver, including Xe, could re-use and add their own individual needs on top
in a next stage. However, this should not block the initial merge.
Dev_coredump
------------
Xe needs to align with other drivers on the way that the error states are
dumped, avoiding a Xe only error_state solution. The goal is to use devcoredump
infrastructure to report error states, since it produces a standardized way
by exposing a virtual and temporary /sys/class/devcoredump device.
As the key measurable result, Xe driver needs to provide GPU snapshots captured
at hang time through devcoredump, but without depending on any core modification
of devcoredump infrastructure itself.
Later, when we are in-tree, the goal is to collaborate with devcoredump
infrastructure with overall possible improvements, like multiple file support
for better organization of the dumps, snapshot support, dmesg extra print,
and whatever may make sense and help the overall infrastructure.
DRM_VM_BIND
-----------
Nouveau, and Xe are all implementing VM_BIND and new Exec uAPIs in order to
fulfill the needs of the modern uAPI. Xe merge should *not* be blocked on the
development of a common new drm_infrastructure. However, the Xe team needs to
engage with the community to explore the options of a common API.
As a key measurable result, the DRM_VM_BIND needs to be documented in this file
below, or this entire block deleted if the consensus is for independent drivers
vm_bind ioctls.
Although having a common DRM level IOCTL for VM_BIND is not a requirement to get
Xe merged, it is mandatory to enforce the overall locking scheme for all major
structs and list (so vm and vma). So, a consensus is needed, and possibly some
common helpers. If helpers are needed, they should be also documented in this
document.
GPU VA
------
Two main goals of Xe are meeting together here:
1) Have an uAPI that aligns with modern UMD needs.
2) Early upstream engagement.
RedHat engineers working on Nouveau proposed a new DRM feature to handle keeping
track of GPU virtual address mappings. This is still not merged upstream, but
this aligns very well with our goals and with our VM_BIND. The engagement with
upstream and the port of Xe towards GPUVA is already ongoing.
As a key measurable result, Xe needs to be aligned with the GPU VA and working in
our tree. Missing Nouveau patches should *not* block Xe and any needed GPUVA
related patch should be independent and present on dri-devel or acked by
maintainers to go along with the first Xe pull request towards drm-next.

View File

@ -120,6 +120,29 @@ Contact: Daniel Vetter, respective driver maintainers
Level: Advanced
Rename drm_atomic_state
-----------------------
The KMS framework uses two slightly different definitions for the ``state``
concept. For a given object (plane, CRTC, encoder, etc., so
``drm_$OBJECT_state``), the state is the entire state of that object. However,
at the device level, ``drm_atomic_state`` refers to a state update for a
limited number of objects.
The state isn't the entire device state, but only the full state of some
objects in that device. This is confusing to newcomers, and
``drm_atomic_state`` should be renamed to something clearer like
``drm_atomic_commit``.
In addition to renaming the structure itself, it would also imply renaming some
related functions (``drm_atomic_state_alloc``, ``drm_atomic_state_get``,
``drm_atomic_state_put``, ``drm_atomic_state_init``,
``__drm_atomic_state_free``, etc.).
Contact: Maxime Ripard <mripard@kernel.org>
Level: Advanced
Fallout from atomic KMS
-----------------------

View File

@ -10467,7 +10467,6 @@ F: drivers/media/rc/img-ir/
IMGTEC POWERVR DRM DRIVER
M: Frank Binns <frank.binns@imgtec.com>
M: Donald Robson <donald.robson@imgtec.com>
M: Matt Coster <matt.coster@imgtec.com>
S: Supported
T: git git://anongit.freedesktop.org/drm/drm-misc

View File

@ -358,8 +358,8 @@ static struct mhi_channel_config aic100_channels[] = {
.wake_capable = false,
},
{
.num = 21,
.name = "QAIC_TIMESYNC",
.num = 21,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
@ -390,8 +390,8 @@ static struct mhi_channel_config aic100_channels[] = {
.wake_capable = false,
},
{
.num = 23,
.name = "QAIC_TIMESYNC_PERIODIC",
.num = 23,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,

View File

@ -30,6 +30,7 @@
#define to_qaic_drm_device(dev) container_of(dev, struct qaic_drm_device, drm)
#define to_drm(qddev) (&(qddev)->drm)
#define to_accel_kdev(qddev) (to_drm(qddev)->accel->kdev) /* Return Linux device of accel node */
#define to_qaic_device(dev) (to_qaic_drm_device((dev))->qdev)
enum __packed dev_states {
/* Device is offline or will be very soon */
@ -191,8 +192,6 @@ struct qaic_bo {
u32 nr_slice;
/* Number of slice that have been transferred by DMA engine */
u32 nr_slice_xfer_done;
/* true = BO is queued for execution, true = BO is not queued */
bool queued;
/*
* If true then user has attached slicing information to this BO by
* calling DRM_IOCTL_QAIC_ATTACH_SLICE_BO ioctl.

View File

@ -141,6 +141,11 @@ struct dbc_rsp {
__le16 status;
} __packed;
static inline bool bo_queued(struct qaic_bo *bo)
{
return !list_empty(&bo->xfer_list);
}
inline int get_dbc_req_elem_size(void)
{
return sizeof(struct dbc_req);
@ -569,6 +574,9 @@ static void qaic_free_sgt(struct sg_table *sgt)
{
struct scatterlist *sg;
if (!sgt)
return;
for (sg = sgt->sgl; sg; sg = sg_next(sg))
if (sg_page(sg))
__free_pages(sg_page(sg), get_order(sg->length));
@ -648,6 +656,7 @@ static void qaic_init_bo(struct qaic_bo *bo, bool reinit)
}
complete_all(&bo->xfer_done);
INIT_LIST_HEAD(&bo->slices);
INIT_LIST_HEAD(&bo->xfer_list);
}
static struct qaic_bo *qaic_alloc_init_bo(void)
@ -709,9 +718,13 @@ int qaic_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *fi
if (ret)
goto free_bo;
ret = drm_gem_create_mmap_offset(obj);
if (ret)
goto free_bo;
ret = drm_gem_handle_create(file_priv, obj, &args->handle);
if (ret)
goto free_sgt;
goto free_bo;
bo->handle = args->handle;
drm_gem_object_put(obj);
@ -720,10 +733,8 @@ int qaic_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *fi
return 0;
free_sgt:
qaic_free_sgt(bo->sgt);
free_bo:
kfree(bo);
drm_gem_object_put(obj);
unlock_dev_srcu:
srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
unlock_usr_srcu:
@ -738,7 +749,7 @@ int qaic_mmap_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file
struct drm_gem_object *obj;
struct qaic_device *qdev;
struct qaic_user *usr;
int ret;
int ret = 0;
usr = file_priv->driver_priv;
usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
@ -760,9 +771,7 @@ int qaic_mmap_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file
goto unlock_dev_srcu;
}
ret = drm_gem_create_mmap_offset(obj);
if (ret == 0)
args->offset = drm_vma_node_offset_addr(&obj->vma_node);
args->offset = drm_vma_node_offset_addr(&obj->vma_node);
drm_gem_object_put(obj);
@ -828,9 +837,6 @@ static int qaic_prepare_import_bo(struct qaic_bo *bo, struct qaic_attach_slice_h
struct sg_table *sgt;
int ret;
if (obj->import_attach->dmabuf->size < hdr->size)
return -EINVAL;
sgt = dma_buf_map_attachment(obj->import_attach, hdr->dir);
if (IS_ERR(sgt)) {
ret = PTR_ERR(sgt);
@ -847,9 +853,6 @@ static int qaic_prepare_export_bo(struct qaic_device *qdev, struct qaic_bo *bo,
{
int ret;
if (bo->base.size < hdr->size)
return -EINVAL;
ret = dma_map_sgtable(&qdev->pdev->dev, bo->sgt, hdr->dir, 0);
if (ret)
return -EFAULT;
@ -950,9 +953,6 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi
if (arg_size / args->hdr.count != sizeof(*slice_ent))
return -EINVAL;
if (args->hdr.size == 0)
return -EINVAL;
if (!(args->hdr.dir == DMA_TO_DEVICE || args->hdr.dir == DMA_FROM_DEVICE))
return -EINVAL;
@ -992,16 +992,16 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi
goto free_slice_ent;
}
ret = qaic_validate_req(qdev, slice_ent, args->hdr.count, args->hdr.size);
if (ret)
goto free_slice_ent;
obj = drm_gem_object_lookup(file_priv, args->hdr.handle);
if (!obj) {
ret = -ENOENT;
goto free_slice_ent;
}
ret = qaic_validate_req(qdev, slice_ent, args->hdr.count, obj->size);
if (ret)
goto put_bo;
bo = to_qaic_bo(obj);
ret = mutex_lock_interruptible(&bo->lock);
if (ret)
@ -1173,7 +1173,6 @@ static int send_bo_list_to_device(struct qaic_device *qdev, struct drm_file *fil
struct bo_slice *slice;
unsigned long flags;
struct qaic_bo *bo;
bool queued;
int i, j;
int ret;
@ -1205,9 +1204,7 @@ static int send_bo_list_to_device(struct qaic_device *qdev, struct drm_file *fil
}
spin_lock_irqsave(&dbc->xfer_lock, flags);
queued = bo->queued;
bo->queued = true;
if (queued) {
if (bo_queued(bo)) {
spin_unlock_irqrestore(&dbc->xfer_lock, flags);
ret = -EINVAL;
goto unlock_bo;
@ -1230,7 +1227,6 @@ static int send_bo_list_to_device(struct qaic_device *qdev, struct drm_file *fil
else
ret = copy_exec_reqs(qdev, slice, dbc->id, head, tail);
if (ret) {
bo->queued = false;
spin_unlock_irqrestore(&dbc->xfer_lock, flags);
goto unlock_bo;
}
@ -1253,8 +1249,7 @@ failed_to_send_bo:
spin_lock_irqsave(&dbc->xfer_lock, flags);
bo = list_last_entry(&dbc->xfer_list, struct qaic_bo, xfer_list);
obj = &bo->base;
bo->queued = false;
list_del(&bo->xfer_list);
list_del_init(&bo->xfer_list);
spin_unlock_irqrestore(&dbc->xfer_lock, flags);
dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
drm_gem_object_put(obj);
@ -1615,8 +1610,7 @@ read_fifo:
*/
dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
bo->nr_slice_xfer_done = 0;
bo->queued = false;
list_del(&bo->xfer_list);
list_del_init(&bo->xfer_list);
bo->perf_stats.req_processed_ts = ktime_get_ns();
complete_all(&bo->xfer_done);
drm_gem_object_put(&bo->base);
@ -1875,7 +1869,7 @@ int qaic_detach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi
/* Check if BO is committed to H/W for DMA */
spin_lock_irqsave(&dbc->xfer_lock, flags);
if (bo->queued) {
if (bo_queued(bo)) {
spin_unlock_irqrestore(&dbc->xfer_lock, flags);
ret = -EBUSY;
goto unlock_ch_srcu;
@ -1905,8 +1899,7 @@ static void empty_xfer_list(struct qaic_device *qdev, struct dma_bridge_chan *db
spin_lock_irqsave(&dbc->xfer_lock, flags);
while (!list_empty(&dbc->xfer_list)) {
bo = list_first_entry(&dbc->xfer_list, typeof(*bo), xfer_list);
bo->queued = false;
list_del(&bo->xfer_list);
list_del_init(&bo->xfer_list);
spin_unlock_irqrestore(&dbc->xfer_lock, flags);
bo->nr_slice_xfer_done = 0;
bo->req_id = 0;

View File

@ -44,6 +44,53 @@ MODULE_PARM_DESC(datapath_polling, "Operate the datapath in polling mode");
static bool link_up;
static DEFINE_IDA(qaic_usrs);
static void qaicm_wq_release(struct drm_device *dev, void *res)
{
struct workqueue_struct *wq = res;
destroy_workqueue(wq);
}
static struct workqueue_struct *qaicm_wq_init(struct drm_device *dev, const char *fmt)
{
struct workqueue_struct *wq;
int ret;
wq = alloc_workqueue(fmt, WQ_UNBOUND, 0);
if (!wq)
return ERR_PTR(-ENOMEM);
ret = drmm_add_action_or_reset(dev, qaicm_wq_release, wq);
if (ret)
return ERR_PTR(ret);
return wq;
}
static void qaicm_srcu_release(struct drm_device *dev, void *res)
{
struct srcu_struct *lock = res;
cleanup_srcu_struct(lock);
}
static int qaicm_srcu_init(struct drm_device *dev, struct srcu_struct *lock)
{
int ret;
ret = init_srcu_struct(lock);
if (ret)
return ret;
return drmm_add_action_or_reset(dev, qaicm_srcu_release, lock);
}
static void qaicm_pci_release(struct drm_device *dev, void *res)
{
struct qaic_device *qdev = to_qaic_device(dev);
pci_set_drvdata(qdev->pdev, NULL);
}
static void free_usr(struct kref *kref)
{
struct qaic_user *usr = container_of(kref, struct qaic_user, ref_count);
@ -299,74 +346,73 @@ void qaic_dev_reset_clean_local_state(struct qaic_device *qdev)
release_dbc(qdev, i);
}
static void cleanup_qdev(struct qaic_device *qdev)
{
int i;
for (i = 0; i < qdev->num_dbc; ++i)
cleanup_srcu_struct(&qdev->dbc[i].ch_lock);
cleanup_srcu_struct(&qdev->dev_lock);
pci_set_drvdata(qdev->pdev, NULL);
destroy_workqueue(qdev->cntl_wq);
destroy_workqueue(qdev->qts_wq);
}
static struct qaic_device *create_qdev(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
struct qaic_drm_device *qddev;
struct qaic_device *qdev;
int i;
struct drm_device *drm;
int i, ret;
qdev = devm_kzalloc(&pdev->dev, sizeof(*qdev), GFP_KERNEL);
qdev = devm_kzalloc(dev, sizeof(*qdev), GFP_KERNEL);
if (!qdev)
return NULL;
qdev->dev_state = QAIC_OFFLINE;
if (id->device == PCI_DEV_AIC100) {
qdev->num_dbc = 16;
qdev->dbc = devm_kcalloc(&pdev->dev, qdev->num_dbc, sizeof(*qdev->dbc), GFP_KERNEL);
qdev->dbc = devm_kcalloc(dev, qdev->num_dbc, sizeof(*qdev->dbc), GFP_KERNEL);
if (!qdev->dbc)
return NULL;
}
qdev->cntl_wq = alloc_workqueue("qaic_cntl", WQ_UNBOUND, 0);
if (!qdev->cntl_wq)
qddev = devm_drm_dev_alloc(&pdev->dev, &qaic_accel_driver, struct qaic_drm_device, drm);
if (IS_ERR(qddev))
return NULL;
qdev->qts_wq = alloc_workqueue("qaic_ts", WQ_UNBOUND, 0);
if (!qdev->qts_wq) {
destroy_workqueue(qdev->cntl_wq);
return NULL;
}
drm = to_drm(qddev);
pci_set_drvdata(pdev, qdev);
qdev->pdev = pdev;
mutex_init(&qdev->cntl_mutex);
ret = drmm_mutex_init(drm, &qddev->users_mutex);
if (ret)
return NULL;
ret = drmm_add_action_or_reset(drm, qaicm_pci_release, NULL);
if (ret)
return NULL;
ret = drmm_mutex_init(drm, &qdev->cntl_mutex);
if (ret)
return NULL;
qdev->cntl_wq = qaicm_wq_init(drm, "qaic_cntl");
if (IS_ERR(qdev->cntl_wq))
return NULL;
qdev->qts_wq = qaicm_wq_init(drm, "qaic_ts");
if (IS_ERR(qdev->qts_wq))
return NULL;
ret = qaicm_srcu_init(drm, &qdev->dev_lock);
if (ret)
return NULL;
qdev->qddev = qddev;
qdev->pdev = pdev;
qddev->qdev = qdev;
INIT_LIST_HEAD(&qdev->cntl_xfer_list);
init_srcu_struct(&qdev->dev_lock);
INIT_LIST_HEAD(&qddev->users);
for (i = 0; i < qdev->num_dbc; ++i) {
spin_lock_init(&qdev->dbc[i].xfer_lock);
qdev->dbc[i].qdev = qdev;
qdev->dbc[i].id = i;
INIT_LIST_HEAD(&qdev->dbc[i].xfer_list);
init_srcu_struct(&qdev->dbc[i].ch_lock);
ret = qaicm_srcu_init(drm, &qdev->dbc[i].ch_lock);
if (ret)
return NULL;
init_waitqueue_head(&qdev->dbc[i].dbc_release);
INIT_LIST_HEAD(&qdev->dbc[i].bo_lists);
}
qddev = devm_drm_dev_alloc(&pdev->dev, &qaic_accel_driver, struct qaic_drm_device, drm);
if (IS_ERR(qddev)) {
cleanup_qdev(qdev);
return NULL;
}
drmm_mutex_init(to_drm(qddev), &qddev->users_mutex);
INIT_LIST_HEAD(&qddev->users);
qddev->qdev = qdev;
qdev->qddev = qddev;
return qdev;
}
@ -472,35 +518,28 @@ static int qaic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ret = init_pci(qdev, pdev);
if (ret)
goto cleanup_qdev;
return ret;
for (i = 0; i < qdev->num_dbc; ++i)
qdev->dbc[i].dbc_base = qdev->bar_2 + QAIC_DBC_OFF(i);
mhi_irq = init_msi(qdev, pdev);
if (mhi_irq < 0) {
ret = mhi_irq;
goto cleanup_qdev;
}
if (mhi_irq < 0)
return mhi_irq;
ret = qaic_create_drm_device(qdev, QAIC_NO_PARTITION);
if (ret)
goto cleanup_qdev;
return ret;
qdev->mhi_cntrl = qaic_mhi_register_controller(pdev, qdev->bar_0, mhi_irq,
qdev->single_msi);
if (IS_ERR(qdev->mhi_cntrl)) {
ret = PTR_ERR(qdev->mhi_cntrl);
goto cleanup_drm_dev;
qaic_destroy_drm_device(qdev, QAIC_NO_PARTITION);
return ret;
}
return 0;
cleanup_drm_dev:
qaic_destroy_drm_device(qdev, QAIC_NO_PARTITION);
cleanup_qdev:
cleanup_qdev(qdev);
return ret;
}
static void qaic_pci_remove(struct pci_dev *pdev)
@ -511,9 +550,8 @@ static void qaic_pci_remove(struct pci_dev *pdev)
return;
qaic_dev_reset_clean_local_state(qdev);
qaic_destroy_drm_device(qdev, QAIC_NO_PARTITION);
qaic_mhi_free_controller(qdev->mhi_cntrl, link_up);
cleanup_qdev(qdev);
qaic_destroy_drm_device(qdev, QAIC_NO_PARTITION);
}
static void qaic_pci_shutdown(struct pci_dev *pdev)

View File

@ -42,7 +42,7 @@ config DRM_MIPI_DSI
config DRM_DEBUG_MM
bool "Insert extra checks and debug info into the DRM range managers"
default n
depends on DRM=y
depends on DRM
depends on STACKTRACE_SUPPORT
select STACKDEPOT
help
@ -289,19 +289,7 @@ config DRM_VGEM
as used by Mesa's software renderer for enhanced performance.
If M is selected the module will be called vgem.
config DRM_VKMS
tristate "Virtual KMS (EXPERIMENTAL)"
depends on DRM && MMU
select DRM_KMS_HELPER
select DRM_GEM_SHMEM_HELPER
select CRC32
default n
help
Virtual Kernel Mode-Setting (VKMS) is used for testing or for
running GPU in a headless machines. Choose this option to get
a VKMS.
If M is selected the module will be called vkms.
source "drivers/gpu/drm/vkms/Kconfig"
source "drivers/gpu/drm/exynos/Kconfig"

View File

@ -2240,11 +2240,13 @@ static void it6505_link_training_work(struct work_struct *work)
ret = it6505_link_start_auto_train(it6505);
DRM_DEV_DEBUG_DRIVER(dev, "auto train %s, auto_train_retry: %d",
ret ? "pass" : "failed", it6505->auto_train_retry);
it6505->auto_train_retry--;
if (ret) {
it6505->auto_train_retry = AUTO_TRAIN_RETRY;
it6505_link_train_ok(it6505);
return;
} else {
it6505->auto_train_retry--;
}
it6505_dump(it6505);

View File

@ -1992,11 +1992,11 @@ int samsung_dsim_probe(struct platform_device *pdev)
else
dsi->bridge.timings = &samsung_dsim_bridge_timings_de_high;
if (dsi->plat_data->host_ops && dsi->plat_data->host_ops->register_host)
if (dsi->plat_data->host_ops && dsi->plat_data->host_ops->register_host) {
ret = dsi->plat_data->host_ops->register_host(dsi);
if (ret)
goto err_disable_runtime;
if (ret)
goto err_disable_runtime;
}
return 0;

View File

@ -41,8 +41,24 @@
/* Registers */
/* DSI D-PHY Layer registers */
#define D0W_DPHYCONTTX 0x0004
#define CLW_DPHYCONTTX 0x0020
#define D0W_DPHYCONTRX 0x0024
#define D1W_DPHYCONTRX 0x0028
#define D2W_DPHYCONTRX 0x002c
#define D3W_DPHYCONTRX 0x0030
#define COM_DPHYCONTRX 0x0038
#define CLW_CNTRL 0x0040
#define D0W_CNTRL 0x0044
#define D1W_CNTRL 0x0048
#define D2W_CNTRL 0x004c
#define D3W_CNTRL 0x0050
#define TESTMODE_CNTRL 0x0054
/* PPI layer registers */
#define PPI_STARTPPI 0x0104 /* START control bit */
#define PPI_BUSYPPI 0x0108 /* PPI busy status */
#define PPI_LPTXTIMECNT 0x0114 /* LPTX timing signal */
#define LPX_PERIOD 3
#define PPI_LANEENABLE 0x0134
@ -59,6 +75,7 @@
/* DSI layer registers */
#define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX */
#define DSI_BUSYDSI 0x0208 /* DSI busy status */
#define DSI_LANEENABLE 0x0210 /* Enables each lane */
#define DSI_RX_START BIT(0)
@ -69,6 +86,20 @@
#define LANEENABLE_L2EN BIT(1)
#define LANEENABLE_L3EN BIT(2)
#define DSI_LANESTATUS0 0x0214 /* DSI lane status 0 */
#define DSI_LANESTATUS1 0x0218 /* DSI lane status 1 */
#define DSI_INTSTATUS 0x0220 /* Interrupt Status */
#define DSI_INTMASK 0x0224 /* Interrupt Mask */
#define DSI_INTCLR 0x0228 /* Interrupt Clear */
#define DSI_LPTXTO 0x0230 /* LPTX Time Out Counter */
/* DSI General Registers */
#define DSIERRCNT 0x0300 /* DSI Error Count Register */
/* DSI Application Layer Registers */
#define APLCTRL 0x0400 /* Application layer Control Register */
#define RDPKTLN 0x0404 /* DSI Read packet Length Register */
/* Display Parallel Input Interface */
#define DPIPXLFMT 0x0440
#define VS_POL_ACTIVE_LOW (1 << 10)
@ -114,35 +145,39 @@
#define VFUEN BIT(0) /* Video Frame Timing Upload */
/* System */
#define TC_IDREG 0x0500
#define SYSSTAT 0x0508
#define SYSCTRL 0x0510
#define DP0_AUDSRC_NO_INPUT (0 << 3)
#define DP0_AUDSRC_I2S_RX (1 << 3)
#define DP0_VIDSRC_NO_INPUT (0 << 0)
#define DP0_VIDSRC_DSI_RX (1 << 0)
#define DP0_VIDSRC_DPI_RX (2 << 0)
#define DP0_VIDSRC_COLOR_BAR (3 << 0)
#define SYSRSTENB 0x050c
#define TC_IDREG 0x0500 /* Chip ID and Revision ID */
#define SYSBOOT 0x0504 /* System BootStrap Status Register */
#define SYSSTAT 0x0508 /* System Status Register */
#define SYSRSTENB 0x050c /* System Reset/Enable Register */
#define ENBI2C (1 << 0)
#define ENBLCD0 (1 << 2)
#define ENBBM (1 << 3)
#define ENBDSIRX (1 << 4)
#define ENBREG (1 << 5)
#define ENBHDCP (1 << 8)
#define GPIOM 0x0540
#define GPIOC 0x0544
#define GPIOO 0x0548
#define GPIOI 0x054c
#define INTCTL_G 0x0560
#define INTSTS_G 0x0564
#define SYSCTRL 0x0510 /* System Control Register */
#define DP0_AUDSRC_NO_INPUT (0 << 3)
#define DP0_AUDSRC_I2S_RX (1 << 3)
#define DP0_VIDSRC_NO_INPUT (0 << 0)
#define DP0_VIDSRC_DSI_RX (1 << 0)
#define DP0_VIDSRC_DPI_RX (2 << 0)
#define DP0_VIDSRC_COLOR_BAR (3 << 0)
#define GPIOM 0x0540 /* GPIO Mode Control Register */
#define GPIOC 0x0544 /* GPIO Direction Control Register */
#define GPIOO 0x0548 /* GPIO Output Register */
#define GPIOI 0x054c /* GPIO Input Register */
#define INTCTL_G 0x0560 /* General Interrupts Control Register */
#define INTSTS_G 0x0564 /* General Interrupts Status Register */
#define INT_SYSERR BIT(16)
#define INT_GPIO_H(x) (1 << (x == 0 ? 2 : 10))
#define INT_GPIO_LC(x) (1 << (x == 0 ? 3 : 11))
#define INT_GP0_LCNT 0x0584
#define INT_GP1_LCNT 0x0588
#define TEST_INT_C 0x0570 /* Test Interrupts Control Register */
#define TEST_INT_S 0x0574 /* Test Interrupts Status Register */
#define INT_GP0_LCNT 0x0584 /* Interrupt GPIO0 Low Count Value Register */
#define INT_GP1_LCNT 0x0588 /* Interrupt GPIO1 Low Count Value Register */
/* Control */
#define DP0CTL 0x0600
@ -152,9 +187,12 @@
#define DP_EN BIT(0) /* Enable DPTX function */
/* Clocks */
#define DP0_VIDMNGEN0 0x0610
#define DP0_VIDMNGEN1 0x0614
#define DP0_VMNGENSTATUS 0x0618
#define DP0_VIDMNGEN0 0x0610 /* DP0 Video Force M Value Register */
#define DP0_VIDMNGEN1 0x0614 /* DP0 Video Force N Value Register */
#define DP0_VMNGENSTATUS 0x0618 /* DP0 Video Current M Value Register */
#define DP0_AUDMNGEN0 0x0628 /* DP0 Audio Force M Value Register */
#define DP0_AUDMNGEN1 0x062c /* DP0 Audio Force N Value Register */
#define DP0_AMNGENSTATUS 0x0630 /* DP0 Audio Current M Value Register */
/* Main Channel */
#define DP0_SECSAMPLE 0x0640
@ -224,8 +262,22 @@
#define DP0_SNKLTCHGREQ 0x06d4
#define DP0_LTLOOPCTRL 0x06d8
#define DP0_SNKLTCTRL 0x06e4
#define DP0_TPATDAT0 0x06e8 /* DP0 Test Pattern bits 29 to 0 */
#define DP0_TPATDAT1 0x06ec /* DP0 Test Pattern bits 59 to 30 */
#define DP0_TPATDAT2 0x06f0 /* DP0 Test Pattern bits 89 to 60 */
#define DP0_TPATDAT3 0x06f4 /* DP0 Test Pattern bits 119 to 90 */
#define DP1_SRCCTRL 0x07a0
#define AUDCFG0 0x0700 /* DP0 Audio Config0 Register */
#define AUDCFG1 0x0704 /* DP0 Audio Config1 Register */
#define AUDIFDATA0 0x0708 /* DP0 Audio Info Frame Bytes 3 to 0 */
#define AUDIFDATA1 0x070c /* DP0 Audio Info Frame Bytes 7 to 4 */
#define AUDIFDATA2 0x0710 /* DP0 Audio Info Frame Bytes 11 to 8 */
#define AUDIFDATA3 0x0714 /* DP0 Audio Info Frame Bytes 15 to 12 */
#define AUDIFDATA4 0x0718 /* DP0 Audio Info Frame Bytes 19 to 16 */
#define AUDIFDATA5 0x071c /* DP0 Audio Info Frame Bytes 23 to 20 */
#define AUDIFDATA6 0x0720 /* DP0 Audio Info Frame Bytes 27 to 24 */
#define DP1_SRCCTRL 0x07a0 /* DP1 Control Register */
/* PHY */
#define DP_PHY_CTRL 0x0800
@ -238,6 +290,25 @@
#define PHY_2LANE BIT(2) /* PHY Enable 2 lanes */
#define PHY_A0_EN BIT(1) /* PHY Aux Channel0 Enable */
#define PHY_M0_EN BIT(0) /* PHY Main Channel0 Enable */
#define DP_PHY_CFG_WR 0x0810 /* DP PHY Configuration Test Write Register */
#define DP_PHY_CFG_RD 0x0814 /* DP PHY Configuration Test Read Register */
#define DP0_AUX_PHY_CTRL 0x0820 /* DP0 AUX PHY Control Register */
#define DP0_MAIN_PHY_DBG 0x0840 /* DP0 Main PHY Test Debug Register */
/* I2S */
#define I2SCFG 0x0880 /* I2S Audio Config 0 Register */
#define I2SCH0STAT0 0x0888 /* I2S Audio Channel 0 Status Bytes 3 to 0 */
#define I2SCH0STAT1 0x088c /* I2S Audio Channel 0 Status Bytes 7 to 4 */
#define I2SCH0STAT2 0x0890 /* I2S Audio Channel 0 Status Bytes 11 to 8 */
#define I2SCH0STAT3 0x0894 /* I2S Audio Channel 0 Status Bytes 15 to 12 */
#define I2SCH0STAT4 0x0898 /* I2S Audio Channel 0 Status Bytes 19 to 16 */
#define I2SCH0STAT5 0x089c /* I2S Audio Channel 0 Status Bytes 23 to 20 */
#define I2SCH1STAT0 0x08a0 /* I2S Audio Channel 1 Status Bytes 3 to 0 */
#define I2SCH1STAT1 0x08a4 /* I2S Audio Channel 1 Status Bytes 7 to 4 */
#define I2SCH1STAT2 0x08a8 /* I2S Audio Channel 1 Status Bytes 11 to 8 */
#define I2SCH1STAT3 0x08ac /* I2S Audio Channel 1 Status Bytes 15 to 12 */
#define I2SCH1STAT4 0x08b0 /* I2S Audio Channel 1 Status Bytes 19 to 16 */
#define I2SCH1STAT5 0x08b4 /* I2S Audio Channel 1 Status Bytes 23 to 20 */
/* PLL */
#define DP0_PLLCTRL 0x0900
@ -1833,16 +1904,16 @@ static bool tc_readable_reg(struct device *dev, unsigned int reg)
case 0x1f4:
/* DSI Protocol Layer */
case DSI_STARTDSI:
case 0x208:
case DSI_BUSYDSI:
case DSI_LANEENABLE:
case 0x214:
case 0x218:
case 0x220:
case DSI_LANESTATUS0:
case DSI_LANESTATUS1:
case DSI_INTSTATUS:
case 0x224:
case 0x228:
case 0x230:
/* DSI General */
case 0x300:
case DSIERRCNT:
/* DSI Application Layer */
case 0x400:
case 0x404:
@ -1978,13 +2049,20 @@ static bool tc_readable_reg(struct device *dev, unsigned int reg)
}
static const struct regmap_range tc_volatile_ranges[] = {
regmap_reg_range(PPI_BUSYPPI, PPI_BUSYPPI),
regmap_reg_range(DSI_BUSYDSI, DSI_BUSYDSI),
regmap_reg_range(DSI_LANESTATUS0, DSI_INTSTATUS),
regmap_reg_range(DSIERRCNT, DSIERRCNT),
regmap_reg_range(VFUEN0, VFUEN0),
regmap_reg_range(SYSSTAT, SYSSTAT),
regmap_reg_range(GPIOI, GPIOI),
regmap_reg_range(INTSTS_G, INTSTS_G),
regmap_reg_range(DP0_VMNGENSTATUS, DP0_VMNGENSTATUS),
regmap_reg_range(DP0_AMNGENSTATUS, DP0_AMNGENSTATUS),
regmap_reg_range(DP0_AUXWDATA(0), DP0_AUXSTATUS),
regmap_reg_range(DP0_LTSTAT, DP0_SNKLTCHGREQ),
regmap_reg_range(DP_PHY_CTRL, DP_PHY_CTRL),
regmap_reg_range(DP0_PLLCTRL, PXL_PLLCTRL),
regmap_reg_range(VFUEN0, VFUEN0),
regmap_reg_range(INTSTS_G, INTSTS_G),
regmap_reg_range(GPIOI, GPIOI),
};
static const struct regmap_access_table tc_volatile_table = {
@ -1992,12 +2070,28 @@ static const struct regmap_access_table tc_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(tc_volatile_ranges),
};
static bool tc_writeable_reg(struct device *dev, unsigned int reg)
{
return (reg != TC_IDREG) &&
(reg != DP0_LTSTAT) &&
(reg != DP0_SNKLTCHGREQ);
}
static const struct regmap_range tc_precious_ranges[] = {
regmap_reg_range(SYSSTAT, SYSSTAT),
};
static const struct regmap_access_table tc_precious_table = {
.yes_ranges = tc_precious_ranges,
.n_yes_ranges = ARRAY_SIZE(tc_precious_ranges),
};
static const struct regmap_range tc_non_writeable_ranges[] = {
regmap_reg_range(PPI_BUSYPPI, PPI_BUSYPPI),
regmap_reg_range(DSI_BUSYDSI, DSI_BUSYDSI),
regmap_reg_range(DSI_LANESTATUS0, DSI_INTSTATUS),
regmap_reg_range(TC_IDREG, SYSSTAT),
regmap_reg_range(GPIOI, GPIOI),
regmap_reg_range(DP0_LTSTAT, DP0_SNKLTCHGREQ),
};
static const struct regmap_access_table tc_writeable_table = {
.no_ranges = tc_non_writeable_ranges,
.n_no_ranges = ARRAY_SIZE(tc_non_writeable_ranges),
};
static const struct regmap_config tc_regmap_config = {
.name = "tc358767",
@ -2008,7 +2102,8 @@ static const struct regmap_config tc_regmap_config = {
.cache_type = REGCACHE_MAPLE,
.readable_reg = tc_readable_reg,
.volatile_table = &tc_volatile_table,
.writeable_reg = tc_writeable_reg,
.precious_table = &tc_precious_table,
.wr_table = &tc_writeable_table,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
};

View File

@ -45,8 +45,6 @@
#include "drm_crtc_internal.h"
#include "drm_internal.h"
#if defined(CONFIG_DEBUG_FS)
/***************************************************
* Initialization, etc.
**************************************************/
@ -647,5 +645,3 @@ void drm_debugfs_encoder_remove(struct drm_encoder *encoder)
debugfs_remove_recursive(encoder->debugfs_entry);
encoder->debugfs_entry = NULL;
}
#endif /* CONFIG_DEBUG_FS */

View File

@ -3611,7 +3611,8 @@ static bool mode_in_range(const struct drm_display_mode *mode,
if (!mode_in_vsync_range(mode, edid, t))
return false;
if ((max_clock = range_pixel_clock(edid, t)))
max_clock = range_pixel_clock(edid, t);
if (max_clock)
if (mode->clock > max_clock)
return false;
@ -6990,28 +6991,6 @@ int drm_add_modes_noedid(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_add_modes_noedid);
/**
* drm_set_preferred_mode - Sets the preferred mode of a connector
* @connector: connector whose mode list should be processed
* @hpref: horizontal resolution of preferred mode
* @vpref: vertical resolution of preferred mode
*
* Marks a mode as preferred if it matches the resolution specified by @hpref
* and @vpref.
*/
void drm_set_preferred_mode(struct drm_connector *connector,
int hpref, int vpref)
{
struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->probed_modes, head) {
if (mode->hdisplay == hpref &&
mode->vdisplay == vpref)
mode->type |= DRM_MODE_TYPE_PREFERRED;
}
}
EXPORT_SYMBOL(drm_set_preferred_mode);
static bool is_hdmi2_sink(const struct drm_connector *connector)
{
/*

View File

@ -229,7 +229,7 @@ typedef struct drm_update_draw32 {
unsigned int num;
/* 64-bit version has a 32-bit pad here */
u64 data; /**< Pointer */
} __attribute__((packed)) drm_update_draw32_t;
} __packed drm_update_draw32_t;
static int compat_drm_update_draw(struct file *file, unsigned int cmd,
unsigned long arg)
@ -296,7 +296,7 @@ typedef struct drm_mode_fb_cmd232 {
u32 pitches[4];
u32 offsets[4];
u64 modifier[4];
} __attribute__((packed)) drm_mode_fb_cmd232_t;
} __packed drm_mode_fb_cmd232_t;
static int compat_drm_mode_addfb2(struct file *file, unsigned int cmd,
unsigned long arg)

View File

@ -2752,3 +2752,25 @@ bool drm_mode_is_420(const struct drm_display_info *display,
drm_mode_is_420_also(display, mode);
}
EXPORT_SYMBOL(drm_mode_is_420);
/**
* drm_set_preferred_mode - Sets the preferred mode of a connector
* @connector: connector whose mode list should be processed
* @hpref: horizontal resolution of preferred mode
* @vpref: vertical resolution of preferred mode
*
* Marks a mode as preferred if it matches the resolution specified by @hpref
* and @vpref.
*/
void drm_set_preferred_mode(struct drm_connector *connector,
int hpref, int vpref)
{
struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->probed_modes, head) {
if (mode->hdisplay == hpref &&
mode->vdisplay == vpref)
mode->type |= DRM_MODE_TYPE_PREFERRED;
}
}
EXPORT_SYMBOL(drm_set_preferred_mode);

View File

@ -1100,42 +1100,6 @@ enum drm_mode_status drm_crtc_helper_mode_valid_fixed(struct drm_crtc *crtc,
}
EXPORT_SYMBOL(drm_crtc_helper_mode_valid_fixed);
/**
* drm_connector_helper_get_modes_from_ddc - Updates the connector's EDID
* property from the connector's
* DDC channel
* @connector: The connector
*
* Returns:
* The number of detected display modes.
*
* Uses a connector's DDC channel to retrieve EDID data and update the
* connector's EDID property and display modes. Drivers can use this
* function to implement struct &drm_connector_helper_funcs.get_modes
* for connectors with a DDC channel.
*/
int drm_connector_helper_get_modes_from_ddc(struct drm_connector *connector)
{
struct edid *edid;
int count = 0;
if (!connector->ddc)
return 0;
edid = drm_get_edid(connector, connector->ddc);
// clears property if EDID is NULL
drm_connector_update_edid_property(connector, edid);
if (edid) {
count = drm_add_edid_modes(connector, edid);
kfree(edid);
}
return count;
}
EXPORT_SYMBOL(drm_connector_helper_get_modes_from_ddc);
/**
* drm_connector_helper_get_modes_fixed - Duplicates a display mode for a connector
* @connector: the connector

View File

@ -18,7 +18,6 @@
#include <linux/i2c-algo-bit.h>
#include <linux/i2c.h>
#include <drm/drm_edid.h>
#include <drm/drm_framebuffer.h>
struct hibmc_connector {

View File

@ -14,6 +14,7 @@
#include <linux/io.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>

View File

@ -146,14 +146,13 @@ int mgag200_device_preinit(struct mga_device *mdev)
}
mdev->vram_res = res;
/* Don't fail on errors, but performance might be reduced. */
devm_arch_io_reserve_memtype_wc(dev->dev, res->start, resource_size(res));
devm_arch_phys_wc_add(dev->dev, res->start, resource_size(res));
mdev->vram = devm_ioremap(dev->dev, res->start, resource_size(res));
mdev->vram = devm_ioremap_wc(dev->dev, res->start, resource_size(res));
if (!mdev->vram)
return -ENOMEM;
/* Don't fail on errors, but performance might be reduced. */
devm_arch_phys_wc_add(dev->dev, res->start, resource_size(res));
return 0;
}

View File

@ -14,13 +14,13 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
@ -717,17 +717,23 @@ void mgag200_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_st
int mgag200_vga_connector_helper_get_modes(struct drm_connector *connector)
{
struct mga_device *mdev = to_mga_device(connector->dev);
int ret;
const struct drm_edid *drm_edid;
int count;
/*
* Protect access to I/O registers from concurrent modesetting
* by acquiring the I/O-register lock.
*/
mutex_lock(&mdev->rmmio_lock);
ret = drm_connector_helper_get_modes_from_ddc(connector);
drm_edid = drm_edid_read(connector);
drm_edid_connector_update(connector, drm_edid);
count = drm_edid_connector_add_modes(connector);
drm_edid_free(drm_edid);
mutex_unlock(&mdev->rmmio_lock);
return ret;
return count;
}
/*

View File

@ -449,7 +449,7 @@ nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
regp->Attribute[NV_CIO_AR_CSEL_INDEX] = 0x00;
}
/**
/*
* Sets up registers for the given mode/adjusted_mode pair.
*
* The clocks, CRTCs and outputs attached to this CRTC must be off.
@ -625,7 +625,7 @@ nv_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
return ret;
}
/**
/*
* Sets up registers for the given mode/adjusted_mode pair.
*
* The clocks, CRTCs and outputs attached to this CRTC must be off.

View File

@ -32,6 +32,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_vblank.h>
#include "nouveau_connector.h"

View File

@ -35,7 +35,6 @@
#include <drm/display/drm_dp_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_util.h>
@ -44,6 +43,7 @@
struct nvkm_i2c_port;
struct dcb_output;
struct edid;
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
struct nouveau_backlight {

View File

@ -1,4 +1,4 @@
/**
/*
* \file mga_ioc32.c
*
* 32-bit ioctl compatibility routines for the MGA DRM.
@ -38,7 +38,7 @@
#include "nouveau_ioctl.h"
/**
/*
* Called whenever a 32-bit process running under a 64-bit kernel
* performs an ioctl on /dev/dri/card<n>.
*

View File

@ -452,13 +452,12 @@ nvif_outp_edid_get(struct nvif_outp *outp, u8 **pedid)
if (ret)
goto done;
*pedid = kmalloc(args->size, GFP_KERNEL);
*pedid = kmemdup(args->data, args->size, GFP_KERNEL);
if (!*pedid) {
ret = -ENOMEM;
goto done;
}
memcpy(*pedid, args->data, args->size);
ret = args->size;
done:
kfree(args);

View File

@ -1040,7 +1040,7 @@ gf100_gr_zbc_init(struct gf100_gr *gr)
}
}
/**
/*
* Wait until GR goes idle. GR is considered idle if it is disabled by the
* MC (0x200) register, or GR is not busy and a context switch is not in
* progress.

View File

@ -575,7 +575,7 @@ init_tmds_reg(struct nvbios_init *init, u8 tmds)
* init opcode handlers
*****************************************************************************/
/**
/*
* init_reserved - stub for various unknown/unused single-byte opcodes
*
*/
@ -602,7 +602,7 @@ init_reserved(struct nvbios_init *init)
init->offset += length;
}
/**
/*
* INIT_DONE - opcode 0x71
*
*/
@ -613,7 +613,7 @@ init_done(struct nvbios_init *init)
init->offset = 0x0000;
}
/**
/*
* INIT_IO_RESTRICT_PROG - opcode 0x32
*
*/
@ -650,7 +650,7 @@ init_io_restrict_prog(struct nvbios_init *init)
trace("}]\n");
}
/**
/*
* INIT_REPEAT - opcode 0x33
*
*/
@ -676,7 +676,7 @@ init_repeat(struct nvbios_init *init)
init->repeat = repeat;
}
/**
/*
* INIT_IO_RESTRICT_PLL - opcode 0x34
*
*/
@ -716,7 +716,7 @@ init_io_restrict_pll(struct nvbios_init *init)
trace("}]\n");
}
/**
/*
* INIT_END_REPEAT - opcode 0x36
*
*/
@ -732,7 +732,7 @@ init_end_repeat(struct nvbios_init *init)
}
}
/**
/*
* INIT_COPY - opcode 0x37
*
*/
@ -759,7 +759,7 @@ init_copy(struct nvbios_init *init)
init_wrvgai(init, port, index, data);
}
/**
/*
* INIT_NOT - opcode 0x38
*
*/
@ -771,7 +771,7 @@ init_not(struct nvbios_init *init)
init_exec_inv(init);
}
/**
/*
* INIT_IO_FLAG_CONDITION - opcode 0x39
*
*/
@ -788,7 +788,7 @@ init_io_flag_condition(struct nvbios_init *init)
init_exec_set(init, false);
}
/**
/*
* INIT_GENERIC_CONDITION - opcode 0x3a
*
*/
@ -840,7 +840,7 @@ init_generic_condition(struct nvbios_init *init)
}
}
/**
/*
* INIT_IO_MASK_OR - opcode 0x3b
*
*/
@ -859,7 +859,7 @@ init_io_mask_or(struct nvbios_init *init)
init_wrvgai(init, 0x03d4, index, data &= ~(1 << or));
}
/**
/*
* INIT_IO_OR - opcode 0x3c
*
*/
@ -878,7 +878,7 @@ init_io_or(struct nvbios_init *init)
init_wrvgai(init, 0x03d4, index, data | (1 << or));
}
/**
/*
* INIT_ANDN_REG - opcode 0x47
*
*/
@ -895,7 +895,7 @@ init_andn_reg(struct nvbios_init *init)
init_mask(init, reg, mask, 0);
}
/**
/*
* INIT_OR_REG - opcode 0x48
*
*/
@ -912,7 +912,7 @@ init_or_reg(struct nvbios_init *init)
init_mask(init, reg, 0, mask);
}
/**
/*
* INIT_INDEX_ADDRESS_LATCHED - opcode 0x49
*
*/
@ -942,7 +942,7 @@ init_idx_addr_latched(struct nvbios_init *init)
}
}
/**
/*
* INIT_IO_RESTRICT_PLL2 - opcode 0x4a
*
*/
@ -977,7 +977,7 @@ init_io_restrict_pll2(struct nvbios_init *init)
trace("}]\n");
}
/**
/*
* INIT_PLL2 - opcode 0x4b
*
*/
@ -994,7 +994,7 @@ init_pll2(struct nvbios_init *init)
init_prog_pll(init, reg, freq);
}
/**
/*
* INIT_I2C_BYTE - opcode 0x4c
*
*/
@ -1025,7 +1025,7 @@ init_i2c_byte(struct nvbios_init *init)
}
}
/**
/*
* INIT_ZM_I2C_BYTE - opcode 0x4d
*
*/
@ -1051,7 +1051,7 @@ init_zm_i2c_byte(struct nvbios_init *init)
}
}
/**
/*
* INIT_ZM_I2C - opcode 0x4e
*
*/
@ -1085,7 +1085,7 @@ init_zm_i2c(struct nvbios_init *init)
}
}
/**
/*
* INIT_TMDS - opcode 0x4f
*
*/
@ -1111,7 +1111,7 @@ init_tmds(struct nvbios_init *init)
init_wr32(init, reg + 0, addr);
}
/**
/*
* INIT_ZM_TMDS_GROUP - opcode 0x50
*
*/
@ -1138,7 +1138,7 @@ init_zm_tmds_group(struct nvbios_init *init)
}
}
/**
/*
* INIT_CR_INDEX_ADDRESS_LATCHED - opcode 0x51
*
*/
@ -1168,7 +1168,7 @@ init_cr_idx_adr_latch(struct nvbios_init *init)
init_wrvgai(init, 0x03d4, addr0, save0);
}
/**
/*
* INIT_CR - opcode 0x52
*
*/
@ -1188,7 +1188,7 @@ init_cr(struct nvbios_init *init)
init_wrvgai(init, 0x03d4, addr, val | data);
}
/**
/*
* INIT_ZM_CR - opcode 0x53
*
*/
@ -1205,7 +1205,7 @@ init_zm_cr(struct nvbios_init *init)
init_wrvgai(init, 0x03d4, addr, data);
}
/**
/*
* INIT_ZM_CR_GROUP - opcode 0x54
*
*/
@ -1229,7 +1229,7 @@ init_zm_cr_group(struct nvbios_init *init)
}
}
/**
/*
* INIT_CONDITION_TIME - opcode 0x56
*
*/
@ -1256,7 +1256,7 @@ init_condition_time(struct nvbios_init *init)
init_exec_set(init, false);
}
/**
/*
* INIT_LTIME - opcode 0x57
*
*/
@ -1273,7 +1273,7 @@ init_ltime(struct nvbios_init *init)
mdelay(msec);
}
/**
/*
* INIT_ZM_REG_SEQUENCE - opcode 0x58
*
*/
@ -1298,7 +1298,7 @@ init_zm_reg_sequence(struct nvbios_init *init)
}
}
/**
/*
* INIT_PLL_INDIRECT - opcode 0x59
*
*/
@ -1317,7 +1317,7 @@ init_pll_indirect(struct nvbios_init *init)
init_prog_pll(init, reg, freq);
}
/**
/*
* INIT_ZM_REG_INDIRECT - opcode 0x5a
*
*/
@ -1336,7 +1336,7 @@ init_zm_reg_indirect(struct nvbios_init *init)
init_wr32(init, addr, data);
}
/**
/*
* INIT_SUB_DIRECT - opcode 0x5b
*
*/
@ -1362,7 +1362,7 @@ init_sub_direct(struct nvbios_init *init)
init->offset += 3;
}
/**
/*
* INIT_JUMP - opcode 0x5c
*
*/
@ -1380,7 +1380,7 @@ init_jump(struct nvbios_init *init)
init->offset += 3;
}
/**
/*
* INIT_I2C_IF - opcode 0x5e
*
*/
@ -1407,7 +1407,7 @@ init_i2c_if(struct nvbios_init *init)
init_exec_force(init, false);
}
/**
/*
* INIT_COPY_NV_REG - opcode 0x5f
*
*/
@ -1433,7 +1433,7 @@ init_copy_nv_reg(struct nvbios_init *init)
init_mask(init, dreg, ~dmask, (data & smask) ^ sxor);
}
/**
/*
* INIT_ZM_INDEX_IO - opcode 0x62
*
*/
@ -1451,7 +1451,7 @@ init_zm_index_io(struct nvbios_init *init)
init_wrvgai(init, port, index, data);
}
/**
/*
* INIT_COMPUTE_MEM - opcode 0x63
*
*/
@ -1469,7 +1469,7 @@ init_compute_mem(struct nvbios_init *init)
init_exec_force(init, false);
}
/**
/*
* INIT_RESET - opcode 0x65
*
*/
@ -1496,7 +1496,7 @@ init_reset(struct nvbios_init *init)
init_exec_force(init, false);
}
/**
/*
* INIT_CONFIGURE_MEM - opcode 0x66
*
*/
@ -1555,7 +1555,7 @@ init_configure_mem(struct nvbios_init *init)
init_exec_force(init, false);
}
/**
/*
* INIT_CONFIGURE_CLK - opcode 0x67
*
*/
@ -1589,7 +1589,7 @@ init_configure_clk(struct nvbios_init *init)
init_exec_force(init, false);
}
/**
/*
* INIT_CONFIGURE_PREINIT - opcode 0x68
*
*/
@ -1615,7 +1615,7 @@ init_configure_preinit(struct nvbios_init *init)
init_exec_force(init, false);
}
/**
/*
* INIT_IO - opcode 0x69
*
*/
@ -1655,7 +1655,7 @@ init_io(struct nvbios_init *init)
init_wrport(init, port, data | value);
}
/**
/*
* INIT_SUB - opcode 0x6b
*
*/
@ -1682,7 +1682,7 @@ init_sub(struct nvbios_init *init)
init->offset += 2;
}
/**
/*
* INIT_RAM_CONDITION - opcode 0x6d
*
*/
@ -1701,7 +1701,7 @@ init_ram_condition(struct nvbios_init *init)
init_exec_set(init, false);
}
/**
/*
* INIT_NV_REG - opcode 0x6e
*
*/
@ -1719,7 +1719,7 @@ init_nv_reg(struct nvbios_init *init)
init_mask(init, reg, ~mask, data);
}
/**
/*
* INIT_MACRO - opcode 0x6f
*
*/
@ -1743,7 +1743,7 @@ init_macro(struct nvbios_init *init)
init->offset += 2;
}
/**
/*
* INIT_RESUME - opcode 0x72
*
*/
@ -1755,7 +1755,7 @@ init_resume(struct nvbios_init *init)
init_exec_set(init, true);
}
/**
/*
* INIT_STRAP_CONDITION - opcode 0x73
*
*/
@ -1773,7 +1773,7 @@ init_strap_condition(struct nvbios_init *init)
init_exec_set(init, false);
}
/**
/*
* INIT_TIME - opcode 0x74
*
*/
@ -1794,7 +1794,7 @@ init_time(struct nvbios_init *init)
}
}
/**
/*
* INIT_CONDITION - opcode 0x75
*
*/
@ -1811,7 +1811,7 @@ init_condition(struct nvbios_init *init)
init_exec_set(init, false);
}
/**
/*
* INIT_IO_CONDITION - opcode 0x76
*
*/
@ -1828,7 +1828,7 @@ init_io_condition(struct nvbios_init *init)
init_exec_set(init, false);
}
/**
/*
* INIT_ZM_REG16 - opcode 0x77
*
*/
@ -1845,7 +1845,7 @@ init_zm_reg16(struct nvbios_init *init)
init_wr32(init, addr, data);
}
/**
/*
* INIT_INDEX_IO - opcode 0x78
*
*/
@ -1867,7 +1867,7 @@ init_index_io(struct nvbios_init *init)
init_wrvgai(init, port, index, data | value);
}
/**
/*
* INIT_PLL - opcode 0x79
*
*/
@ -1884,7 +1884,7 @@ init_pll(struct nvbios_init *init)
init_prog_pll(init, reg, freq);
}
/**
/*
* INIT_ZM_REG - opcode 0x7a
*
*/
@ -1904,7 +1904,7 @@ init_zm_reg(struct nvbios_init *init)
init_wr32(init, addr, data);
}
/**
/*
* INIT_RAM_RESTRICT_PLL - opcde 0x87
*
*/
@ -1934,7 +1934,7 @@ init_ram_restrict_pll(struct nvbios_init *init)
}
}
/**
/*
* INIT_RESET_BEGUN - opcode 0x8c
*
*/
@ -1945,7 +1945,7 @@ init_reset_begun(struct nvbios_init *init)
init->offset += 1;
}
/**
/*
* INIT_RESET_END - opcode 0x8d
*
*/
@ -1956,7 +1956,7 @@ init_reset_end(struct nvbios_init *init)
init->offset += 1;
}
/**
/*
* INIT_GPIO - opcode 0x8e
*
*/
@ -1972,7 +1972,7 @@ init_gpio(struct nvbios_init *init)
nvkm_gpio_reset(gpio, DCB_GPIO_UNUSED);
}
/**
/*
* INIT_RAM_RESTRICT_ZM_GROUP - opcode 0x8f
*
*/
@ -2010,7 +2010,7 @@ init_ram_restrict_zm_reg_group(struct nvbios_init *init)
}
}
/**
/*
* INIT_COPY_ZM_REG - opcode 0x90
*
*/
@ -2027,7 +2027,7 @@ init_copy_zm_reg(struct nvbios_init *init)
init_wr32(init, dreg, init_rd32(init, sreg));
}
/**
/*
* INIT_ZM_REG_GROUP - opcode 0x91
*
*/
@ -2049,7 +2049,7 @@ init_zm_reg_group(struct nvbios_init *init)
}
}
/**
/*
* INIT_XLAT - opcode 0x96
*
*/
@ -2077,7 +2077,7 @@ init_xlat(struct nvbios_init *init)
init_mask(init, daddr, ~dmask, data);
}
/**
/*
* INIT_ZM_MASK_ADD - opcode 0x97
*
*/
@ -2098,7 +2098,7 @@ init_zm_mask_add(struct nvbios_init *init)
init_wr32(init, addr, data);
}
/**
/*
* INIT_AUXCH - opcode 0x98
*
*/
@ -2122,7 +2122,7 @@ init_auxch(struct nvbios_init *init)
}
}
/**
/*
* INIT_AUXCH - opcode 0x99
*
*/
@ -2144,7 +2144,7 @@ init_zm_auxch(struct nvbios_init *init)
}
}
/**
/*
* INIT_I2C_LONG_IF - opcode 0x9a
*
*/
@ -2183,7 +2183,7 @@ init_i2c_long_if(struct nvbios_init *init)
init_exec_set(init, false);
}
/**
/*
* INIT_GPIO_NE - opcode 0xa9
*
*/

View File

@ -45,7 +45,7 @@ static const struct cvb_coef gk20a_cvb_coef[] = {
/* 852 */ { 1608418, -21643, -269, 0, 763, -48},
};
/**
/*
* cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0)
*/
static inline int
@ -58,7 +58,7 @@ gk20a_volt_get_cvb_voltage(int speedo, int s_scale, const struct cvb_coef *coef)
return mv;
}
/**
/*
* cvb_t_mv =
* ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) +
* ((c3 * speedo / s_scale + c4 + c5 * T / t_scale) * T / t_scale)

View File

@ -70,6 +70,21 @@ struct panel_delay {
*/
unsigned int hpd_absent;
/**
* @powered_on_to_enable: Time between panel powered on and enable.
*
* The minimum time, in milliseconds, that needs to have passed
* between when panel powered on and enable may begin.
*
* This is (T3+T4+T5+T6+T8)-min on eDP timing diagrams or after the
* power supply enabled until we can turn the backlight on and see
* valid data.
*
* This doesn't normally need to be set if timings are already met by
* prepare_to_enable or enable.
*/
unsigned int powered_on_to_enable;
/**
* @prepare_to_enable: Time between prepare and enable.
*
@ -216,6 +231,7 @@ struct panel_edp {
bool prepared;
ktime_t prepared_time;
ktime_t powered_on_time;
ktime_t unprepared_time;
const struct panel_desc *desc;
@ -413,8 +429,7 @@ static int panel_edp_unprepare(struct drm_panel *panel)
if (!p->prepared)
return 0;
pm_runtime_mark_last_busy(panel->dev);
ret = pm_runtime_put_autosuspend(panel->dev);
ret = pm_runtime_put_sync_suspend(panel->dev);
if (ret < 0)
return ret;
p->prepared = false;
@ -455,6 +470,8 @@ static int panel_edp_prepare_once(struct panel_edp *p)
gpiod_set_value_cansleep(p->enable_gpio, 1);
p->powered_on_time = ktime_get_boottime();
delay = p->desc->delay.hpd_reliable;
if (p->no_hpd)
delay = max(delay, p->desc->delay.hpd_absent);
@ -579,6 +596,8 @@ static int panel_edp_enable(struct drm_panel *panel)
panel_edp_wait(p->prepared_time, p->desc->delay.prepare_to_enable);
panel_edp_wait(p->powered_on_time, p->desc->delay.powered_on_to_enable);
p->enabled = true;
return 0;
@ -1837,6 +1856,13 @@ static const struct panel_delay delay_200_500_p2e80 = {
.prepare_to_enable = 80,
};
static const struct panel_delay delay_200_500_e50_p2e80 = {
.hpd_absent = 200,
.unprepare = 500,
.enable = 50,
.prepare_to_enable = 80,
};
static const struct panel_delay delay_200_500_p2e100 = {
.hpd_absent = 200,
.unprepare = 500,
@ -1874,6 +1900,13 @@ static const struct panel_delay delay_200_500_e200 = {
.enable = 200,
};
static const struct panel_delay delay_200_500_e200_d200 = {
.hpd_absent = 200,
.unprepare = 500,
.enable = 200,
.disable = 200,
};
static const struct panel_delay delay_200_500_e200_d10 = {
.hpd_absent = 200,
.unprepare = 500,
@ -1887,6 +1920,13 @@ static const struct panel_delay delay_200_150_e200 = {
.enable = 200,
};
static const struct panel_delay delay_200_500_e50_po2e200 = {
.hpd_absent = 200,
.unprepare = 500,
.enable = 50,
.powered_on_to_enable = 200,
};
#define EDP_PANEL_ENTRY(vend_chr_0, vend_chr_1, vend_chr_2, product_id, _delay, _name) \
{ \
.name = _name, \
@ -1912,7 +1952,9 @@ static const struct panel_delay delay_200_150_e200 = {
* Sort first by vendor, then by product ID.
*/
static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0x105c, &delay_200_500_e50, "B116XTN01.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1062, &delay_200_500_e50, "B120XAN01.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x125c, &delay_200_500_e50, "Unknown"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x145c, &delay_200_500_e50, "B116XAB01.4"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1e9b, &delay_200_500_e50, "B133UAN02.1"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1ea5, &delay_200_500_e50, "B116XAK01.6"),
@ -1923,54 +1965,91 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0x403d, &delay_200_500_e50, "B140HAN04.0"),
EDP_PANEL_ENTRY2('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01.0",
&auo_b116xa3_mode),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x435c, &delay_200_500_e50, "Unknown"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x582d, &delay_200_500_e50, "B133UAN01.0"),
EDP_PANEL_ENTRY2('A', 'U', 'O', 0x615c, &delay_200_500_e50, "B116XAN06.1",
&auo_b116xa3_mode),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x635c, &delay_200_500_e50, "B116XAN06.3"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x639c, &delay_200_500_e50, "B140HAK02.7"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x723c, &delay_200_500_e50, "B140XTN07.2"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x8594, &delay_200_500_e50, "B133UAN01.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0xf390, &delay_200_500_e50, "B140XTN07.7"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0607, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0608, &delay_200_500_e50, "NT116WHM-N11"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0668, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x068f, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x06e5, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0705, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0715, &delay_200_150_e200, "NT116WHM-N21"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0717, &delay_200_500_e50_po2e200, "NV133FHM-N42"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0731, &delay_200_500_e80, "NT116WHM-N42"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0741, &delay_200_500_e200, "NT116WHM-N44"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0744, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x074c, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0751, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0754, &delay_200_500_e50_po2e200, "NV116WHM-N45"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0771, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0786, &delay_200_500_p2e80, "NV116WHM-T01"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0797, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x07d1, &boe_nv133fhm_n61.delay, "NV133FHM-N61"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x07d3, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x07f6, &delay_200_500_e200, "NT140FHM-N44"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x07f8, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0813, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0827, &delay_200_500_e50_p2e80, "NT140WHM-N44 V8.0"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x082d, &boe_nv133fhm_n61.delay, "NV133FHM-N62"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0843, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x08b2, &delay_200_500_e200, "NT140WHM-N49"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0848, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0849, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x09c3, &delay_200_500_e50, "NT116WHM-N21,836X2"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x094b, &delay_200_500_e50, "NT116WHM-N21"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0951, &delay_200_500_e80, "NV116WHM-N47"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x095f, &delay_200_500_e50, "NE135FBM-N41 v8.1"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x096e, &delay_200_500_e50_po2e200, "NV116WHM-T07 V8.0"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0979, &delay_200_500_e50, "NV116WHM-N49 V8.0"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x098d, &boe_nv110wtm_n61.delay, "NV110WTM-N61"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0993, &delay_200_500_e80, "NV116WHM-T14 V8.0"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x09ad, &delay_200_500_e80, "NV116WHM-N47"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x09ae, &delay_200_500_e200, "NT140FHM-N45"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x09dd, &delay_200_500_e50, "NT116WHM-N21"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a36, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a3e, &delay_200_500_e80, "NV116WHM-N49"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a5d, &delay_200_500_e50, "NV116WHM-N45"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ac5, &delay_200_500_e50, "NV116WHM-N4C"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b34, &delay_200_500_e80, "NV122WUM-N41"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b43, &delay_200_500_e200, "NV140FHM-T09"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b56, &delay_200_500_e80, "NT140FHM-N47"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c20, &delay_200_500_e80, "NT140FHM-N47"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1130, &delay_200_500_e50, "N116BGE-EB2"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1132, &delay_200_500_e80_d50, "N116BGE-EA2"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1138, &innolux_n116bca_ea1.delay, "N116BCA-EA1-RC4"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1139, &delay_200_500_e80_d50, "N116BGE-EA2"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1141, &delay_200_500_e80_d50, "Unknown"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1145, &delay_200_500_e80_d50, "N116BCN-EB1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x114a, &delay_200_500_e80_d50, "Unknown"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x114c, &innolux_n116bca_ea1.delay, "N116BCA-EA1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1152, &delay_200_500_e80_d50, "N116BCN-EA1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1153, &delay_200_500_e80_d50, "N116BGE-EA2"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1154, &delay_200_500_e80_d50, "N116BCA-EA2"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1156, &delay_200_500_e80_d50, "Unknown"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1157, &delay_200_500_e80_d50, "N116BGE-EA2"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x115b, &delay_200_500_e80_d50, "N116BCN-EB1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x142b, &delay_200_500_e80_d50, "N140HCA-EAC"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x142e, &delay_200_500_e80_d50, "N140BGA-EA4"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x144f, &delay_200_500_e80_d50, "N140HGA-EA1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1468, &delay_200_500_e80, "N140HGA-EA1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d4, &delay_200_500_e80_d50, "N140HCA-EAC"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d6, &delay_200_500_e80_d50, "N140BGA-EA4"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x14e5, &delay_200_500_e80_d50, "N140HGA-EA1"),
EDP_PANEL_ENTRY('C', 'S', 'O', 0x1200, &delay_200_500_e50, "MNC207QS1-1"),
EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d51, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d5b, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d5c, &delay_200_500_e200, "MB116AN01-2"),
EDP_PANEL_ENTRY('I', 'V', 'O', 0x048e, &delay_200_500_e200_d10, "M116NWR6 R5"),
@ -1979,11 +2058,25 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('I', 'V', 'O', 0x854b, &delay_200_500_p2e100, "R133NW4K-R0"),
EDP_PANEL_ENTRY('I', 'V', 'O', 0x8c4d, &delay_200_150_e200, "R140NWFM R1"),
EDP_PANEL_ENTRY('K', 'D', 'B', 0x044f, &delay_200_500_e80_d50, "Unknown"),
EDP_PANEL_ENTRY('K', 'D', 'B', 0x0624, &kingdisplay_kd116n21_30nv_a010.delay, "116N21-30NV-A010"),
EDP_PANEL_ENTRY('K', 'D', 'B', 0x1118, &delay_200_500_e50, "KD116N29-30NK-A005"),
EDP_PANEL_ENTRY('K', 'D', 'B', 0x1120, &delay_200_500_e80_d50, "116N29-30NK-C007"),
EDP_PANEL_ENTRY('K', 'D', 'C', 0x044f, &delay_200_500_e50, "KD116N9-30NH-F3"),
EDP_PANEL_ENTRY('K', 'D', 'C', 0x05f1, &delay_200_500_e80_d50, "KD116N5-30NV-G7"),
EDP_PANEL_ENTRY('K', 'D', 'C', 0x0809, &delay_200_500_e50, "KD116N2930A15"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x0000, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x048d, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x0497, &delay_200_500_e200_d200, "LP116WH7-SPB1"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x052c, &delay_200_500_e200_d200, "LP133WF2-SPL7"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x0537, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x054a, &delay_200_500_e200_d200, "LP116WH8-SPC1"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x0567, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x05af, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x05f1, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('S', 'D', 'C', 0x416d, &delay_100_500_e200, "ATNA45AF01"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1511, &delay_200_500_e50, "LQ140M1JW48"),

View File

@ -646,26 +646,17 @@ static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
return -EINVAL;
ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset_gpio)) {
dev_err(dev, "cannot get reset gpio\n");
return PTR_ERR(ctx->reset_gpio);
}
if (IS_ERR(ctx->reset_gpio))
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "cannot get reset gpio\n");
ctx->vci = devm_regulator_get(dev, "vci");
if (IS_ERR(ctx->vci)) {
ret = PTR_ERR(ctx->vci);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to request vci regulator: %d\n", ret);
return ret;
}
if (IS_ERR(ctx->vci))
return dev_err_probe(dev, PTR_ERR(ctx->vci), "Failed to request vci regulator\n");
ctx->iovcc = devm_regulator_get(dev, "iovcc");
if (IS_ERR(ctx->iovcc)) {
ret = PTR_ERR(ctx->iovcc);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to request iovcc regulator: %d\n", ret);
return ret;
}
if (IS_ERR(ctx->iovcc))
return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
"Failed to request iovcc regulator\n");
mipi_dsi_set_drvdata(dsi, ctx);

View File

@ -36,6 +36,9 @@
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
#define NT35510_CMD_CORRECT_GAMMA BIT(0)
#define NT35510_CMD_CONTROL_DISPLAY BIT(1)
#define MCS_CMD_MAUCCTR 0xF0 /* Manufacturer command enable */
#define MCS_CMD_READ_ID1 0xDA
#define MCS_CMD_READ_ID2 0xDB
@ -112,18 +115,33 @@
/* AVDD and AVEE setting 3 bytes */
#define NT35510_P1_AVDD_LEN 3
#define NT35510_P1_AVEE_LEN 3
#define NT35510_P1_VCL_LEN 3
#define NT35510_P1_VGH_LEN 3
#define NT35510_P1_VGL_LEN 3
#define NT35510_P1_VGP_LEN 3
#define NT35510_P1_VGN_LEN 3
#define NT35510_P1_VCMOFF_LEN 2
/* BT1CTR thru BT5CTR setting 3 bytes */
#define NT35510_P1_BT1CTR_LEN 3
#define NT35510_P1_BT2CTR_LEN 3
#define NT35510_P1_BT3CTR_LEN 3
#define NT35510_P1_BT4CTR_LEN 3
#define NT35510_P1_BT5CTR_LEN 3
/* 52 gamma parameters times two per color: positive and negative */
#define NT35510_P1_GAMMA_LEN 52
#define NT35510_WRCTRLD_BCTRL BIT(5)
#define NT35510_WRCTRLD_A BIT(4)
#define NT35510_WRCTRLD_DD BIT(3)
#define NT35510_WRCTRLD_BL BIT(2)
#define NT35510_WRCTRLD_DB BIT(1)
#define NT35510_WRCTRLD_G BIT(0)
#define NT35510_WRCABC_OFF 0
#define NT35510_WRCABC_UI_MODE 1
#define NT35510_WRCABC_STILL_MODE 2
#define NT35510_WRCABC_MOVING_MODE 3
/**
* struct nt35510_config - the display-specific NT35510 configuration
*
@ -171,6 +189,14 @@ struct nt35510_config {
* timing in the display controller.
*/
const struct drm_display_mode mode;
/**
* @mode_flags: DSI operation mode related flags
*/
unsigned long mode_flags;
/**
* @cmds: enable DSI commands
*/
u32 cmds;
/**
* @avdd: setting for AVDD ranging from 0x00 = 6.5V to 0x14 = 4.5V
* in 0.1V steps the default is 0x05 which means 6.0V
@ -220,6 +246,25 @@ struct nt35510_config {
* The defaults are 4 and 3 yielding 0x34
*/
u8 bt2ctr[NT35510_P1_BT2CTR_LEN];
/**
* @vcl: setting for VCL ranging from 0x00 = -2.5V to 0x11 = -4.0V
* in 1V steps, the default is 0x00 which means -2.5V
*/
u8 vcl[NT35510_P1_VCL_LEN];
/**
* @bt3ctr: setting for boost power control for the VCL step-up
* circuit (3)
* bits 0..2 in the lower nibble controls CLCK, the booster clock
* frequency, the values are the same as for PCK in @bt1ctr.
* bits 4..5 in the upper nibble controls BTCL, the boosting
* amplification for the step-up circuit.
* 0 = Disable
* 1 = -0.5 x VDDB
* 2 = -1 x VDDB
* 3 = -2 x VDDB
* The defaults are 4 and 2 yielding 0x24
*/
u8 bt3ctr[NT35510_P1_BT3CTR_LEN];
/**
* @vgh: setting for VGH ranging from 0x00 = 7.0V to 0x0B = 18.0V
* in 1V steps, the default is 0x08 which means 15V
@ -273,6 +318,113 @@ struct nt35510_config {
* same layout of bytes as @vgp.
*/
u8 vgn[NT35510_P1_VGN_LEN];
/**
* @vcmoff: setting the DC VCOM offset voltage
* The first byte contains bit 8 of VCM in bit 0 and VCMOFFSEL in bit 4.
* The second byte contains bits 0..7 of VCM.
* VCMOFFSEL the common voltage offset mode.
* VCMOFFSEL 0x00 = VCOM .. 0x01 Gamma.
* The default is 0x00.
* VCM the VCOM output voltage (VCMOFFSEL = 0) or the internal register
* offset for gamma voltage (VCMOFFSEL = 1).
* VCM 0x00 = 0V/0 .. 0x118 = 3.5V/280 in steps of 12.5mV/1step
* The default is 0x00 = 0V/0.
*/
u8 vcmoff[NT35510_P1_VCMOFF_LEN];
/**
* @dopctr: setting optional control for display
* ERR bits 0..1 in the first byte is the ERR pin output signal setting.
* 0 = Disable, ERR pin output low
* 1 = ERR pin output CRC error only
* 2 = ERR pin output ECC error only
* 3 = ERR pin output CRC and ECC error
* The default is 0.
* N565 bit 2 in the first byte is the 16-bit/pixel format selection.
* 0 = R[4:0] + G[5:3] & G[2:0] + B[4:0]
* 1 = G[2:0] + R[4:0] & B[4:0] + G[5:3]
* The default is 0.
* DIS_EoTP_HS bit 3 in the first byte is "DSI protocol violation" error
* reporting.
* 0 = reporting when error
* 1 = not reporting when error
* DSIM bit 4 in the first byte is the video mode data type enable
* 0 = Video mode data type disable
* 1 = Video mode data type enable
* The default is 0.
* DSIG bit 5 int the first byte is the generic r/w data type enable
* 0 = Generic r/w disable
* 1 = Generic r/w enable
* The default is 0.
* DSITE bit 6 in the first byte is TE line enable
* 0 = TE line is disabled
* 1 = TE line is enabled
* The default is 0.
* RAMKP bit 7 in the first byte is the frame memory keep/loss in
* sleep-in mode
* 0 = contents loss in sleep-in
* 1 = contents keep in sleep-in
* The default is 0.
* CRL bit 1 in the second byte is the source driver data shift
* direction selection. This bit is XOR operation with bit RSMX
* of 3600h command.
* 0 (RMSX = 0) = S1 -> S1440
* 0 (RMSX = 1) = S1440 -> S1
* 1 (RMSX = 0) = S1440 -> S1
* 1 (RMSX = 1) = S1 -> S1440
* The default is 0.
* CTB bit 2 in the second byte is the vertical scanning direction
* selection for gate control signals. This bit is XOR operation
* with bit ML of 3600h command.
* 0 (ML = 0) = Forward (top -> bottom)
* 0 (ML = 1) = Reverse (bottom -> top)
* 1 (ML = 0) = Reverse (bottom -> top)
* 1 (ML = 1) = Forward (top -> bottom)
* The default is 0.
* CRGB bit 3 in the second byte is RGB-BGR order selection. This
* bit is XOR operation with bit RGB of 3600h command.
* 0 (RGB = 0) = RGB/Normal
* 0 (RGB = 1) = BGR/RB swap
* 1 (RGB = 0) = BGR/RB swap
* 1 (RGB = 1) = RGB/Normal
* The default is 0.
* TE_PWR_SEL bit 4 in the second byte is the TE output voltage
* level selection (only valid when DSTB_SEL = 0 or DSTB_SEL = 1,
* VSEL = High and VDDI = 1.665~3.3V).
* 0 = TE output voltage level is VDDI
* 1 = TE output voltage level is VDDA
* The default is 0.
*/
u8 dopctr[NT35510_P0_DOPCTR_LEN];
/**
* @madctl: Memory data access control
* RSMY bit 0 is flip vertical. Flips the display image top to down.
* RSMX bit 1 is flip horizontal. Flips the display image left to right.
* MH bit 2 is the horizontal refresh order.
* RGB bit 3 is the RGB-BGR order.
* 0 = RGB color sequence
* 1 = BGR color sequence
* ML bit 4 is the vertical refresh order.
* MV bit 5 is the row/column exchange.
* MX bit 6 is the column address order.
* MY bit 7 is the row address order.
*/
u8 madctl;
/**
* @sdhdtctr: source output data hold time
* 0x00..0x3F = 0..31.5us in steps of 0.5us
* The default is 0x05 = 2.5us.
*/
u8 sdhdtctr;
/**
* @gseqctr: EQ control for gate signals
* GFEQ_XX[3:0]: time setting of EQ step for falling edge in steps
* of 0.5us.
* The default is 0x07 = 3.5us
* GREQ_XX[7:4]: time setting of EQ step for rising edge in steps
* of 0.5us.
* The default is 0x07 = 3.5us
*/
u8 gseqctr[NT35510_P0_GSEQCTR_LEN];
/**
* @sdeqctr: Source driver control settings, first byte is
* 0 for mode 1 and 1 for mode 2. Mode 1 uses two steps and
@ -343,6 +495,43 @@ struct nt35510_config {
* @gamma_corr_neg_b: Blue gamma correction parameters, negative
*/
u8 gamma_corr_neg_b[NT35510_P1_GAMMA_LEN];
/**
* @wrdisbv: write display brightness
* 0x00 value means the lowest brightness and 0xff value means
* the highest brightness.
* The default is 0x00.
*/
u8 wrdisbv;
/**
* @wrctrld: write control display
* G bit 0 selects gamma curve: 0 = Manual, 1 = Automatic
* DB bit 1 selects display brightness: 0 = Manual, 1 = Automatic
* BL bit 2 controls backlight control: 0 = Off, 1 = On
* DD bit 3 controls display dimming: 0 = Off, 1 = On
* A bit 4 controls LABC block: 0 = Off, 1 = On
* BCTRL bit 5 controls brightness block: 0 = Off, 1 = On
*/
u8 wrctrld;
/**
* @wrcabc: write content adaptive brightness control
* There is possible to use 4 different modes for content adaptive
* image functionality:
* 0: Off
* 1: User Interface Image (UI-Mode)
* 2: Still Picture Image (Still-Mode)
* 3: Moving Picture Image (Moving-Mode)
* The default is 0
*/
u8 wrcabc;
/**
* @wrcabcmb: write CABC minimum brightness
* Set the minimum brightness value of the display for CABC
* function.
* 0x00 value means the lowest brightness for CABC and 0xff
* value means the highest brightness for CABC.
* The default is 0x00.
*/
u8 wrcabcmb;
};
/**
@ -486,6 +675,16 @@ static int nt35510_setup_power(struct nt35510 *nt)
nt->conf->bt2ctr);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVCL,
NT35510_P1_VCL_LEN,
nt->conf->vcl);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_BT3CTR,
NT35510_P1_BT3CTR_LEN,
nt->conf->bt3ctr);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVGH,
NT35510_P1_VGH_LEN,
nt->conf->vgh);
@ -522,6 +721,12 @@ static int nt35510_setup_power(struct nt35510 *nt)
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SETVCMOFF,
NT35510_P1_VCMOFF_LEN,
nt->conf->vcmoff);
if (ret)
return ret;
/* Typically 10 ms */
usleep_range(10000, 20000);
@ -536,46 +741,28 @@ static int nt35510_setup_display(struct nt35510 *nt)
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
const struct nt35510_config *conf = nt->conf;
u8 dopctr[NT35510_P0_DOPCTR_LEN];
u8 gseqctr[NT35510_P0_GSEQCTR_LEN];
u8 dpfrctr[NT35510_P0_DPFRCTR1_LEN];
/* FIXME: set up any rotation (assume none for now) */
u8 addr_mode = NT35510_ROTATE_0_SETTING;
u8 val;
int ret;
/* Enable TE, EoTP and RGB pixel format */
dopctr[0] = NT35510_DOPCTR_0_DSITE | NT35510_DOPCTR_0_EOTP |
NT35510_DOPCTR_0_N565;
dopctr[1] = NT35510_DOPCTR_1_CTB;
ret = nt35510_send_long(nt, dsi, NT35510_P0_DOPCTR,
NT35510_P0_DOPCTR_LEN,
dopctr);
conf->dopctr);
if (ret)
return ret;
ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &addr_mode,
sizeof(addr_mode));
ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &conf->madctl,
sizeof(conf->madctl));
if (ret < 0)
return ret;
/*
* Source data hold time, default 0x05 = 2.5us
* 0x00..0x3F = 0 .. 31.5us in steps of 0.5us
* 0x0A = 5us
*/
val = 0x0A;
ret = mipi_dsi_dcs_write(dsi, NT35510_P0_SDHDTCTR, &val,
sizeof(val));
ret = mipi_dsi_dcs_write(dsi, NT35510_P0_SDHDTCTR, &conf->sdhdtctr,
sizeof(conf->sdhdtctr));
if (ret < 0)
return ret;
/* EQ control for gate signals, 0x00 = 0 us */
gseqctr[0] = 0x00;
gseqctr[1] = 0x00;
ret = nt35510_send_long(nt, dsi, NT35510_P0_GSEQCTR,
NT35510_P0_GSEQCTR_LEN,
gseqctr);
conf->gseqctr);
if (ret)
return ret;
@ -719,36 +906,38 @@ static int nt35510_power_on(struct nt35510 *nt)
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_RED_POS,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_pos_r);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_GREEN_POS,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_pos_g);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_BLUE_POS,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_pos_b);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_RED_NEG,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_neg_r);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_GREEN_NEG,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_neg_g);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_BLUE_NEG,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_neg_b);
if (ret)
return ret;
if (nt->conf->cmds & NT35510_CMD_CORRECT_GAMMA) {
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_RED_POS,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_pos_r);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_GREEN_POS,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_pos_g);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_BLUE_POS,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_pos_b);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_RED_NEG,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_neg_r);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_GREEN_NEG,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_neg_g);
if (ret)
return ret;
ret = nt35510_send_long(nt, dsi, NT35510_P1_SET_GAMMA_BLUE_NEG,
NT35510_P1_GAMMA_LEN,
nt->conf->gamma_corr_neg_b);
if (ret)
return ret;
}
/* Set up stuff in manufacturer control, page 0 */
ret = nt35510_send_long(nt, dsi, MCS_CMD_MAUCCTR,
@ -827,6 +1016,26 @@ static int nt35510_prepare(struct drm_panel *panel)
/* Up to 120 ms */
usleep_range(120000, 150000);
if (nt->conf->cmds & NT35510_CMD_CONTROL_DISPLAY) {
ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
&nt->conf->wrctrld,
sizeof(nt->conf->wrctrld));
if (ret < 0)
return ret;
ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE,
&nt->conf->wrcabc,
sizeof(nt->conf->wrcabc));
if (ret < 0)
return ret;
ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS,
&nt->conf->wrcabcmb,
sizeof(nt->conf->wrcabcmb));
if (ret < 0)
return ret;
}
ret = mipi_dsi_dcs_set_display_on(dsi);
if (ret) {
dev_err(nt->dev, "failed to turn display on (%d)\n", ret);
@ -896,7 +1105,6 @@ static int nt35510_probe(struct mipi_dsi_device *dsi)
*/
dsi->hs_rate = 349440000;
dsi->lp_rate = 9600000;
dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
/*
* Every new incarnation of this display must have a unique
@ -908,6 +1116,8 @@ static int nt35510_probe(struct mipi_dsi_device *dsi)
return -ENODEV;
}
dsi->mode_flags = nt->conf->mode_flags;
nt->supplies[0].supply = "vdd"; /* 2.3-4.8 V */
nt->supplies[1].supply = "vddi"; /* 1.65-3.3V */
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(nt->supplies),
@ -923,7 +1133,7 @@ static int nt35510_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
nt->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
nt->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(nt->reset_gpio)) {
dev_err(dev, "error getting RESET GPIO\n");
return PTR_ERR(nt->reset_gpio);
@ -952,7 +1162,10 @@ static int nt35510_probe(struct mipi_dsi_device *dsi)
return PTR_ERR(bl);
}
bl->props.max_brightness = 255;
bl->props.brightness = 255;
if (nt->conf->cmds & NT35510_CMD_CONTROL_DISPLAY)
bl->props.brightness = nt->conf->wrdisbv;
else
bl->props.brightness = 255;
bl->props.power = FB_BLANK_POWERDOWN;
nt->panel.backlight = bl;
}
@ -1030,6 +1243,8 @@ static const struct nt35510_config nt35510_hydis_hva40wv1 = {
.vtotal = 800 + 2 + 0 + 5, /* VBP = 5 */
.flags = 0,
},
.mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS,
.cmds = NT35510_CMD_CORRECT_GAMMA,
/* 0x09: AVDD = 5.6V */
.avdd = { 0x09, 0x09, 0x09 },
/* 0x34: PCK = Hsync/2, BTP = 2 x VDDB */
@ -1038,6 +1253,10 @@ static const struct nt35510_config nt35510_hydis_hva40wv1 = {
.avee = { 0x09, 0x09, 0x09 },
/* 0x24: NCK = Hsync/2, BTN = -2 x VDDB */
.bt2ctr = { 0x24, 0x24, 0x24 },
/* VBCLA: -2.5V, VBCLB: -2.5V, VBCLC: -2.5V */
.vcl = { 0x00, 0x00, 0x00 },
/* 0x24: CLCK = Hsync/2, BTN = -1 x VDDB */
.bt3ctr = { 0x24, 0x24, 0x24 },
/* 0x05 = 12V */
.vgh = { 0x05, 0x05, 0x05 },
/* 0x24: NCKA = Hsync/2, VGH = 2 x AVDD - AVEE */
@ -1050,6 +1269,16 @@ static const struct nt35510_config nt35510_hydis_hva40wv1 = {
.vgp = { 0x00, 0xA3, 0x00 },
/* VGMP: 0x0A3 = 5.0375V, VGSP = 0V */
.vgn = { 0x00, 0xA3, 0x00 },
/* VCMOFFSEL = VCOM voltage offset mode, VCM = 0V */
.vcmoff = { 0x00, 0x00 },
/* Enable TE, EoTP and RGB pixel format */
.dopctr = { NT35510_DOPCTR_0_DSITE | NT35510_DOPCTR_0_EOTP |
NT35510_DOPCTR_0_N565, NT35510_DOPCTR_1_CTB },
.madctl = NT35510_ROTATE_0_SETTING,
/* 0x0A: SDT = 5 us */
.sdhdtctr = 0x0A,
/* EQ control for gate signals, 0x00 = 0 us */
.gseqctr = { 0x00, 0x00 },
/* SDEQCTR: source driver EQ mode 2, 2.5 us rise time on each step */
.sdeqctr = { 0x01, 0x05, 0x05, 0x05 },
/* SDVPCTR: Normal operation off color during v porch */
@ -1073,7 +1302,88 @@ static const struct nt35510_config nt35510_hydis_hva40wv1 = {
.gamma_corr_neg_b = { NT35510_GAMMA_NEG_DEFAULT },
};
static const struct nt35510_config nt35510_frida_frd400b25025 = {
.width_mm = 52,
.height_mm = 86,
.mode = {
.clock = 23000,
.hdisplay = 480,
.hsync_start = 480 + 34, /* HFP = 34 */
.hsync_end = 480 + 34 + 2, /* HSync = 2 */
.htotal = 480 + 34 + 2 + 34, /* HBP = 34 */
.vdisplay = 800,
.vsync_start = 800 + 15, /* VFP = 15 */
.vsync_end = 800 + 15 + 12, /* VSync = 12 */
.vtotal = 800 + 15 + 12 + 15, /* VBP = 15 */
.flags = 0,
},
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM,
.cmds = NT35510_CMD_CONTROL_DISPLAY,
/* 0x03: AVDD = 6.2V */
.avdd = { 0x03, 0x03, 0x03 },
/* 0x46: PCK = 2 x Hsync, BTP = 2.5 x VDDB */
.bt1ctr = { 0x46, 0x46, 0x46 },
/* 0x03: AVEE = -6.2V */
.avee = { 0x03, 0x03, 0x03 },
/* 0x36: PCK = 2 x Hsync, BTP = 2 x VDDB */
.bt2ctr = { 0x36, 0x36, 0x36 },
/* VBCLA: -2.5V, VBCLB: -2.5V, VBCLC: -3.5V */
.vcl = { 0x00, 0x00, 0x02 },
/* 0x26: CLCK = 2 x Hsync, BTN = -1 x VDDB */
.bt3ctr = { 0x26, 0x26, 0x26 },
/* 0x09 = 16V */
.vgh = { 0x09, 0x09, 0x09 },
/* 0x36: HCK = 2 x Hsync, VGH = 2 x AVDD - AVEE */
.bt4ctr = { 0x36, 0x36, 0x36 },
/* 0x08 = -10V */
.vgl = { 0x08, 0x08, 0x08 },
/* 0x26: LCK = 2 x Hsync, VGL = AVDD + VCL - AVDD */
.bt5ctr = { 0x26, 0x26, 0x26 },
/* VGMP: 0x080 = 4.6V, VGSP = 0V */
.vgp = { 0x00, 0x80, 0x00 },
/* VGMP: 0x080 = 4.6V, VGSP = 0V */
.vgn = { 0x00, 0x80, 0x00 },
/* VCMOFFSEL = VCOM voltage offset mode, VCM = -1V */
.vcmoff = { 0x00, 0x50 },
.dopctr = { NT35510_DOPCTR_0_RAMKP | NT35510_DOPCTR_0_DSITE |
NT35510_DOPCTR_0_DSIG | NT35510_DOPCTR_0_DSIM |
NT35510_DOPCTR_0_EOTP | NT35510_DOPCTR_0_N565, 0 },
.madctl = NT35510_ROTATE_180_SETTING,
/* 0x03: SDT = 1.5 us */
.sdhdtctr = 0x03,
/* EQ control for gate signals, 0x00 = 0 us */
.gseqctr = { 0x00, 0x00 },
/* SDEQCTR: source driver EQ mode 2, 1 us rise time on each step */
.sdeqctr = { 0x01, 0x02, 0x02, 0x02 },
/* SDVPCTR: Normal operation off color during v porch */
.sdvpctr = 0x01,
/* T1: number of pixel clocks on one scanline: 0x184 = 389 clocks */
.t1 = 0x0184,
/* VBP: vertical back porch toward the panel */
.vbp = 0x1C,
/* VFP: vertical front porch toward the panel */
.vfp = 0x1C,
/* PSEL: divide pixel clock 23MHz with 1 (no clock downscaling) */
.psel = 0,
/* DPTMCTR12: 0x03: LVGL = VGLX, overlap mode, swap R->L O->E */
.dpmctr12 = { 0x03, 0x00, 0x00, },
/* write display brightness */
.wrdisbv = 0x7f,
/* write control display */
.wrctrld = NT35510_WRCTRLD_BCTRL | NT35510_WRCTRLD_DD |
NT35510_WRCTRLD_BL,
/* write content adaptive brightness control */
.wrcabc = NT35510_WRCABC_STILL_MODE,
/* write CABC minimum brightness */
.wrcabcmb = 0xff,
};
static const struct of_device_id nt35510_of_match[] = {
{
.compatible = "frida,frd400b25025",
.data = &nt35510_frida_frd400b25025,
},
{
.compatible = "hydis,hva40wv1",
.data = &nt35510_hydis_hva40wv1,

View File

@ -343,6 +343,9 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
return ret;
}
rockchip_drm_encoder_set_crtc_endpoint_id(&dp->encoder,
dev->of_node, 0, 0);
dp->plat_data.encoder = &dp->encoder.encoder;
ret = analogix_dp_bind(dp->adp, drm_dev);

View File

@ -10,7 +10,6 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/hdmi.h>
#include <linux/mfd/syscon.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
@ -26,12 +25,17 @@
#include "inno_hdmi.h"
struct hdmi_data_info {
int vic;
bool sink_has_audio;
unsigned int enc_in_format;
unsigned int enc_out_format;
unsigned int colorimetry;
#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U
struct inno_hdmi_phy_config {
unsigned long pixelclock;
u8 pre_emphasis;
u8 voltage_level_control;
};
struct inno_hdmi_variant {
struct inno_hdmi_phy_config *phy_configs;
struct inno_hdmi_phy_config *default_phy_config;
};
struct inno_hdmi_i2c {
@ -46,10 +50,9 @@ struct inno_hdmi_i2c {
struct inno_hdmi {
struct device *dev;
struct drm_device *drm_dev;
int irq;
struct clk *pclk;
struct clk *refclk;
void __iomem *regs;
struct drm_connector connector;
@ -58,10 +61,14 @@ struct inno_hdmi {
struct inno_hdmi_i2c *i2c;
struct i2c_adapter *ddc;
unsigned int tmds_rate;
const struct inno_hdmi_variant *variant;
};
struct hdmi_data_info hdmi_data;
struct drm_display_mode previous_mode;
struct inno_hdmi_connector_state {
struct drm_connector_state base;
unsigned int enc_out_format;
unsigned int colorimetry;
bool rgb_limited_range;
};
static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
@ -76,50 +83,16 @@ static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
return container_of(connector, struct inno_hdmi, connector);
}
#define to_inno_hdmi_conn_state(conn_state) \
container_of_const(conn_state, struct inno_hdmi_connector_state, base)
enum {
CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
CSC_RGB_0_255_TO_RGB_16_235_8BIT,
};
static const char coeff_csc[][24] = {
/*
* YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
* R = 1.164*Y + 1.596*V - 204
* G = 1.164*Y - 0.391*U - 0.813*V + 154
* B = 1.164*Y + 2.018*U - 258
*/
{
0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
},
/*
* YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
* R = Y + 1.402*V - 248
* G = Y - 0.344*U - 0.714*V + 135
* B = Y + 1.772*U - 227
*/
{
0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
},
/*
* YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
* R = 1.164*Y + 1.793*V - 248
* G = 1.164*Y - 0.213*U - 0.534*V + 77
* B = 1.164*Y + 2.115*U - 289
*/
{
0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
},
/*
* RGB2YUV:601 SD mode:
* Cb = -0.291G - 0.148R + 0.439B + 128
@ -155,6 +128,36 @@ static const char coeff_csc[][24] = {
},
};
static struct inno_hdmi_phy_config rk3036_hdmi_phy_configs[] = {
{ 74250000, 0x3f, 0xbb },
{ 165000000, 0x6f, 0xbb },
{ ~0UL, 0x00, 0x00 }
};
static struct inno_hdmi_phy_config rk3128_hdmi_phy_configs[] = {
{ 74250000, 0x3f, 0xaa },
{ 165000000, 0x5f, 0xaa },
{ ~0UL, 0x00, 0x00 }
};
static int inno_hdmi_find_phy_config(struct inno_hdmi *hdmi,
unsigned long pixelclk)
{
const struct inno_hdmi_phy_config *phy_configs =
hdmi->variant->phy_configs;
int i;
for (i = 0; phy_configs[i].pixelclock != ~0UL; i++) {
if (pixelclk <= phy_configs[i].pixelclock)
return i;
}
DRM_DEV_DEBUG(hdmi->dev, "No phy configuration for pixelclock %lu\n",
pixelclk);
return -EINVAL;
}
static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
{
return readl_relaxed(hdmi->regs + (offset) * 0x04);
@ -174,11 +177,11 @@ static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
hdmi_writeb(hdmi, offset, temp);
}
static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi, unsigned long long rate)
{
int ddc_bus_freq;
unsigned long long ddc_bus_freq = rate >> 2;
ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
do_div(ddc_bus_freq, HDMI_SCL_RATE);
hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
@ -196,38 +199,44 @@ static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
}
static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
static void inno_hdmi_standby(struct inno_hdmi *hdmi)
{
switch (mode) {
case NORMAL:
inno_hdmi_sys_power(hdmi, false);
inno_hdmi_sys_power(hdmi, false);
hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
};
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
static void inno_hdmi_power_up(struct inno_hdmi *hdmi,
unsigned long mpixelclock)
{
struct inno_hdmi_phy_config *phy_config;
int ret = inno_hdmi_find_phy_config(hdmi, mpixelclock);
inno_hdmi_sys_power(hdmi, true);
break;
case LOWER_PWR:
inno_hdmi_sys_power(hdmi, false);
hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
break;
default:
DRM_DEV_ERROR(hdmi->dev, "Unknown power mode %d\n", mode);
if (ret < 0) {
phy_config = hdmi->variant->default_phy_config;
DRM_DEV_ERROR(hdmi->dev,
"Using default phy configuration for TMDS rate %lu",
mpixelclock);
} else {
phy_config = &hdmi->variant->phy_configs[ret];
}
}
inno_hdmi_sys_power(hdmi, false);
hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, phy_config->pre_emphasis);
hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->voltage_level_control);
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
inno_hdmi_sys_power(hdmi, true);
};
static void inno_hdmi_reset(struct inno_hdmi *hdmi)
{
@ -244,75 +253,96 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
inno_hdmi_standby(hdmi);
}
static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
union hdmi_infoframe *frame, u32 frame_index,
u32 mask, u32 disable, u32 enable)
static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi,
enum hdmi_infoframe_type type)
{
if (mask)
hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
struct drm_connector *connector = &hdmi->connector;
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
if (setup_rc >= 0) {
u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
ssize_t rc, i;
rc = hdmi_infoframe_pack(frame, packed_frame,
sizeof(packed_frame));
if (rc < 0)
return rc;
for (i = 0; i < rc; i++)
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
packed_frame[i]);
if (mask)
hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev,
"Unsupported infoframe type: %u\n", type);
return;
}
return setup_rc;
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
}
static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
union hdmi_infoframe *frame, enum hdmi_infoframe_type type)
{
union hdmi_infoframe frame;
int rc;
struct drm_connector *connector = &hdmi->connector;
u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
ssize_t rc, i;
rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
&hdmi->connector,
mode);
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev,
"Unsupported infoframe type: %u\n", type);
return 0;
}
return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
inno_hdmi_disable_frame(hdmi, type);
rc = hdmi_infoframe_pack(frame, packed_frame,
sizeof(packed_frame));
if (rc < 0)
return rc;
for (i = 0; i < rc; i++)
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
packed_frame[i]);
return 0;
}
static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
{
struct drm_connector *connector = &hdmi->connector;
struct drm_connector_state *conn_state = connector->state;
struct inno_hdmi_connector_state *inno_conn_state =
to_inno_hdmi_conn_state(conn_state);
union hdmi_infoframe frame;
int rc;
rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
&hdmi->connector,
mode);
if (rc) {
inno_hdmi_disable_frame(hdmi, HDMI_INFOFRAME_TYPE_AVI);
return rc;
}
if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444)
frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
else if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV422)
frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
else
frame.avi.colorspace = HDMI_COLORSPACE_RGB;
return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) {
drm_hdmi_avi_infoframe_quant_range(&frame.avi,
connector, mode,
inno_conn_state->rgb_limited_range ?
HDMI_QUANTIZATION_RANGE_LIMITED :
HDMI_QUANTIZATION_RANGE_FULL);
} else {
frame.avi.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
frame.avi.ycc_quantization_range =
HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
}
return inno_hdmi_upload_frame(hdmi, &frame, HDMI_INFOFRAME_TYPE_AVI);
}
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
{
struct hdmi_data_info *data = &hdmi->hdmi_data;
struct drm_connector *connector = &hdmi->connector;
struct drm_connector_state *conn_state = connector->state;
struct inno_hdmi_connector_state *inno_conn_state =
to_inno_hdmi_conn_state(conn_state);
int c0_c2_change = 0;
int csc_enable = 0;
int csc_mode = 0;
@ -330,9 +360,14 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
v_VIDEO_INPUT_CSP(0);
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
if (data->enc_in_format == data->enc_out_format) {
if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
(data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) {
if (inno_conn_state->rgb_limited_range) {
csc_mode = CSC_RGB_0_255_TO_RGB_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
} else {
value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
@ -342,35 +377,21 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
return 0;
}
}
if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
(data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
(data->enc_out_format == HDMI_COLORSPACE_RGB)) {
csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
auto_csc = AUTO_CSC_ENABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_DISABLE;
}
} else {
if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
(data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
(data->enc_out_format == HDMI_COLORSPACE_RGB)) {
csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
auto_csc = AUTO_CSC_ENABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_DISABLE;
if (inno_conn_state->colorimetry == HDMI_COLORIMETRY_ITU_601) {
if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
}
} else {
if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
}
}
}
@ -411,7 +432,7 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
value = mode->hsync_start - mode->hdisplay;
value = mode->htotal - mode->hsync_start;
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
@ -426,7 +447,7 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
value = mode->vtotal - mode->vdisplay;
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
value = mode->vsync_start - mode->vdisplay;
value = mode->vtotal - mode->vsync_start;
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
value = mode->vsync_end - mode->vsync_start;
@ -443,19 +464,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
{
struct drm_display_info *display = &hdmi->connector.display_info;
hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
(hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
(hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
(hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
else
hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
unsigned long mpixelclock = mode->clock * 1000;
/* Mute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
@ -469,10 +478,8 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
inno_hdmi_config_video_csc(hdmi);
if (display->is_hdmi) {
if (display->is_hdmi)
inno_hdmi_config_video_avi(hdmi, mode);
inno_hdmi_config_video_vsi(hdmi, mode);
}
/*
* When IP controller have configured to an accurate video
@ -480,47 +487,73 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
* DCLK_LCDC, so we need to init the TMDS rate to mode pixel
* clock rate, and reconfigure the DDC clock.
*/
hdmi->tmds_rate = mode->clock * 1000;
inno_hdmi_i2c_init(hdmi);
inno_hdmi_i2c_init(hdmi, mpixelclock);
/* Unmute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
inno_hdmi_power_up(hdmi, mpixelclock);
return 0;
}
static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adj_mode)
static enum drm_mode_status inno_hdmi_display_mode_valid(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
{
unsigned long mpixelclk, max_tolerance;
long rounded_refclk;
/* No support for double-clock modes */
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
return MODE_BAD;
mpixelclk = mode->clock * 1000;
if (mpixelclk < INNO_HDMI_MIN_TMDS_CLOCK)
return MODE_CLOCK_LOW;
if (inno_hdmi_find_phy_config(hdmi, mpixelclk) < 0)
return MODE_CLOCK_HIGH;
if (hdmi->refclk) {
rounded_refclk = clk_round_rate(hdmi->refclk, mpixelclk);
if (rounded_refclk < 0)
return MODE_BAD;
/* Vesa DMT standard mentions +/- 0.5% max tolerance */
max_tolerance = mpixelclk / 200;
if (abs_diff((unsigned long)rounded_refclk, mpixelclk) > max_tolerance)
return MODE_NOCLOCK;
}
return MODE_OK;
}
static void inno_hdmi_encoder_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
struct drm_connector_state *conn_state;
struct drm_crtc_state *crtc_state;
conn_state = drm_atomic_get_new_connector_state(state, &hdmi->connector);
if (WARN_ON(!conn_state))
return;
crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
if (WARN_ON(!crtc_state))
return;
inno_hdmi_setup(hdmi, &crtc_state->adjusted_mode);
}
static void inno_hdmi_encoder_disable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
inno_hdmi_setup(hdmi, adj_mode);
/* Store the display mode for plugin/DPMS poweron events */
drm_mode_copy(&hdmi->previous_mode, adj_mode);
}
static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
{
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}
static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
{
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
}
static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adj_mode)
{
return true;
inno_hdmi_standby(hdmi);
}
static int
@ -529,19 +562,35 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_connector_state *conn_state)
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
u8 vic = drm_match_cea_mode(mode);
struct inno_hdmi_connector_state *inno_conn_state =
to_inno_hdmi_conn_state(conn_state);
s->output_mode = ROCKCHIP_OUT_MODE_P888;
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
return 0;
if (vic == 6 || vic == 7 ||
vic == 21 || vic == 22 ||
vic == 2 || vic == 3 ||
vic == 17 || vic == 18)
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_601;
else
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
inno_conn_state->enc_out_format = HDMI_COLORSPACE_RGB;
inno_conn_state->rgb_limited_range =
drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
return inno_hdmi_display_mode_valid(hdmi,
&crtc_state->adjusted_mode) == MODE_OK ? 0 : -EINVAL;
}
static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
.enable = inno_hdmi_encoder_enable,
.disable = inno_hdmi_encoder_disable,
.mode_fixup = inno_hdmi_encoder_mode_fixup,
.mode_set = inno_hdmi_encoder_mode_set,
.atomic_check = inno_hdmi_encoder_atomic_check,
.atomic_check = inno_hdmi_encoder_atomic_check,
.atomic_enable = inno_hdmi_encoder_enable,
.atomic_disable = inno_hdmi_encoder_disable,
};
static enum drm_connector_status
@ -564,7 +613,6 @@ static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
edid = drm_get_edid(connector, hdmi->ddc);
if (edid) {
hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
drm_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
@ -577,14 +625,9 @@ static enum drm_mode_status
inno_hdmi_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return MODE_OK;
}
struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
static int
inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
uint32_t maxX, uint32_t maxY)
{
return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
return inno_hdmi_display_mode_valid(hdmi, mode);
}
static void inno_hdmi_connector_destroy(struct drm_connector *connector)
@ -593,13 +636,64 @@ static void inno_hdmi_connector_destroy(struct drm_connector *connector)
drm_connector_cleanup(connector);
}
static void
inno_hdmi_connector_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state)
{
struct inno_hdmi_connector_state *inno_conn_state =
to_inno_hdmi_conn_state(state);
__drm_atomic_helper_connector_destroy_state(&inno_conn_state->base);
kfree(inno_conn_state);
}
static void inno_hdmi_connector_reset(struct drm_connector *connector)
{
struct inno_hdmi_connector_state *inno_conn_state;
if (connector->state) {
inno_hdmi_connector_destroy_state(connector, connector->state);
connector->state = NULL;
}
inno_conn_state = kzalloc(sizeof(*inno_conn_state), GFP_KERNEL);
if (!inno_conn_state)
return;
__drm_atomic_helper_connector_reset(connector, &inno_conn_state->base);
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
inno_conn_state->enc_out_format = HDMI_COLORSPACE_RGB;
inno_conn_state->rgb_limited_range = false;
}
static struct drm_connector_state *
inno_hdmi_connector_duplicate_state(struct drm_connector *connector)
{
struct inno_hdmi_connector_state *inno_conn_state;
if (WARN_ON(!connector->state))
return NULL;
inno_conn_state = kmemdup(to_inno_hdmi_conn_state(connector->state),
sizeof(*inno_conn_state), GFP_KERNEL);
if (!inno_conn_state)
return NULL;
__drm_atomic_helper_connector_duplicate_state(connector,
&inno_conn_state->base);
return &inno_conn_state->base;
}
static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
.fill_modes = inno_hdmi_probe_single_connector_modes,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = inno_hdmi_connector_detect,
.destroy = inno_hdmi_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.reset = inno_hdmi_connector_reset,
.atomic_duplicate_state = inno_hdmi_connector_duplicate_state,
.atomic_destroy_state = inno_hdmi_connector_destroy_state,
};
static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
@ -819,6 +913,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = data;
struct inno_hdmi *hdmi;
const struct inno_hdmi_variant *variant;
int irq;
int ret;
@ -827,7 +922,12 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
return -ENOMEM;
hdmi->dev = dev;
hdmi->drm_dev = drm;
variant = of_device_get_match_data(hdmi->dev);
if (!variant)
return -EINVAL;
hdmi->variant = variant;
hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hdmi->regs))
@ -846,6 +946,20 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
return ret;
}
hdmi->refclk = devm_clk_get_optional(hdmi->dev, "ref");
if (IS_ERR(hdmi->refclk)) {
DRM_DEV_ERROR(hdmi->dev, "Unable to get HDMI reference clock\n");
ret = PTR_ERR(hdmi->refclk);
goto err_disable_pclk;
}
ret = clk_prepare_enable(hdmi->refclk);
if (ret) {
DRM_DEV_ERROR(hdmi->dev,
"Cannot enable HDMI reference clock: %d\n", ret);
goto err_disable_pclk;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
@ -862,13 +976,16 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
}
/*
* When IP controller haven't configured to an accurate video
* timing, then the TMDS clock source would be switched to
* PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
* and reconfigure the DDC clock.
* When the controller isn't configured to an accurate
* video timing and there is no reference clock available,
* then the TMDS clock source would be switched to PCLK_HDMI,
* so we need to init the TMDS rate to PCLK rate, and
* reconfigure the DDC clock.
*/
hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
inno_hdmi_i2c_init(hdmi);
if (hdmi->refclk)
inno_hdmi_i2c_init(hdmi, clk_get_rate(hdmi->refclk));
else
inno_hdmi_i2c_init(hdmi, clk_get_rate(hdmi->pclk));
ret = inno_hdmi_register(drm, hdmi);
if (ret)
@ -892,6 +1009,8 @@ err_cleanup_hdmi:
err_put_adapter:
i2c_put_adapter(hdmi->ddc);
err_disable_clk:
clk_disable_unprepare(hdmi->refclk);
err_disable_pclk:
clk_disable_unprepare(hdmi->pclk);
return ret;
}
@ -905,6 +1024,7 @@ static void inno_hdmi_unbind(struct device *dev, struct device *master,
hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
i2c_put_adapter(hdmi->ddc);
clk_disable_unprepare(hdmi->refclk);
clk_disable_unprepare(hdmi->pclk);
}
@ -923,8 +1043,22 @@ static void inno_hdmi_remove(struct platform_device *pdev)
component_del(&pdev->dev, &inno_hdmi_ops);
}
static const struct inno_hdmi_variant rk3036_inno_hdmi_variant = {
.phy_configs = rk3036_hdmi_phy_configs,
.default_phy_config = &rk3036_hdmi_phy_configs[1],
};
static const struct inno_hdmi_variant rk3128_inno_hdmi_variant = {
.phy_configs = rk3128_hdmi_phy_configs,
.default_phy_config = &rk3128_hdmi_phy_configs[1],
};
static const struct of_device_id inno_hdmi_dt_ids[] = {
{ .compatible = "rockchip,rk3036-inno-hdmi",
.data = &rk3036_inno_hdmi_variant,
},
{ .compatible = "rockchip,rk3128-inno-hdmi",
.data = &rk3128_inno_hdmi_variant,
},
{},
};

View File

@ -10,11 +10,6 @@
#define DDC_SEGMENT_ADDR 0x30
enum PWR_MODE {
NORMAL,
LOWER_PWR,
};
#define HDMI_SCL_RATE (100*1000)
#define DDC_BUS_FREQ_L 0x4b
#define DDC_BUS_FREQ_H 0x4c

View File

@ -576,8 +576,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
ret = -EINVAL;
goto err_put_port;
} else if (ret) {
DRM_DEV_ERROR(dev, "failed to find panel and bridge node\n");
ret = -EPROBE_DEFER;
dev_err_probe(dev, ret, "failed to find panel and bridge node\n");
goto err_put_port;
}
if (lvds->panel)

View File

@ -227,11 +227,22 @@ static const struct vop_win_data rk3126_vop_win_data[] = {
.type = DRM_PLANE_TYPE_CURSOR },
};
static const struct vop_output rk3126_output = {
.pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4),
.hdmi_pin_pol = VOP_REG(RK3126_INT_SCALER, 0x7, 4),
.hdmi_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 22),
.hdmi_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 23),
.rgb_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 24),
.rgb_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 25),
.mipi_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 28),
.mipi_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 29),
};
static const struct vop_data rk3126_vop = {
.intr = &rk3036_intr,
.common = &rk3036_common,
.modeset = &rk3036_modeset,
.output = &rk3036_output,
.output = &rk3126_output,
.win = rk3126_vop_win_data,
.win_size = ARRAY_SIZE(rk3126_vop_win_data),
.max_output = { 1920, 1080 },

View File

@ -872,6 +872,9 @@
/* rk3036 register definition end */
/* rk3126 register definition */
#define RK3126_INT_SCALER 0x0c
/* win1 register */
#define RK3126_WIN1_MST 0x4c
#define RK3126_WIN1_DSP_INFO 0x50
#define RK3126_WIN1_DSP_ST 0x54

View File

@ -1248,7 +1248,7 @@ int drm_sched_init(struct drm_gpu_scheduler *sched,
long timeout, struct workqueue_struct *timeout_wq,
atomic_t *score, const char *name, struct device *dev)
{
int i, ret;
int i;
sched->ops = ops;
sched->credit_limit = credit_limit;
@ -1284,11 +1284,11 @@ int drm_sched_init(struct drm_gpu_scheduler *sched,
sched->own_submit_wq = true;
}
ret = -ENOMEM;
sched->sched_rq = kmalloc_array(num_rqs, sizeof(*sched->sched_rq),
GFP_KERNEL | __GFP_ZERO);
if (!sched->sched_rq)
goto Out_free;
goto Out_check_own;
sched->num_rqs = num_rqs;
for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) {
sched->sched_rq[i] = kzalloc(sizeof(*sched->sched_rq[i]), GFP_KERNEL);
@ -1313,13 +1313,14 @@ int drm_sched_init(struct drm_gpu_scheduler *sched,
Out_unroll:
for (--i ; i >= DRM_SCHED_PRIORITY_KERNEL; i--)
kfree(sched->sched_rq[i]);
Out_free:
kfree(sched->sched_rq);
sched->sched_rq = NULL;
Out_check_own:
if (sched->own_submit_wq)
destroy_workqueue(sched->submit_wq);
drm_err(sched, "%s: Failed to setup GPU scheduler--out of memory\n", __func__);
return ret;
return -ENOMEM;
}
EXPORT_SYMBOL(drm_sched_init);

View File

@ -142,6 +142,11 @@ static const struct of_device_id ssd130x_of_match[] = {
.compatible = "solomon,ssd1327",
.data = &ssd130x_variants[SSD1327_ID],
},
/* ssd133x family */
{
.compatible = "solomon,ssd1331",
.data = &ssd130x_variants[SSD1331_ID],
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ssd130x_of_match);
@ -166,6 +171,8 @@ static const struct spi_device_id ssd130x_spi_table[] = {
{ "ssd1322", SSD1322_ID },
{ "ssd1325", SSD1325_ID },
{ "ssd1327", SSD1327_ID },
/* ssd133x family */
{ "ssd1331", SSD1331_ID },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, ssd130x_spi_table);

View File

@ -119,6 +119,26 @@
#define SSD130X_SET_VCOMH_VOLTAGE 0xbe
#define SSD132X_SET_FUNCTION_SELECT_B 0xd5
/* ssd133x commands */
#define SSD133X_SET_COL_RANGE 0x15
#define SSD133X_SET_ROW_RANGE 0x75
#define SSD133X_CONTRAST_A 0x81
#define SSD133X_CONTRAST_B 0x82
#define SSD133X_CONTRAST_C 0x83
#define SSD133X_SET_MASTER_CURRENT 0x87
#define SSD132X_SET_PRECHARGE_A 0x8a
#define SSD132X_SET_PRECHARGE_B 0x8b
#define SSD132X_SET_PRECHARGE_C 0x8c
#define SSD133X_SET_DISPLAY_START 0xa1
#define SSD133X_SET_DISPLAY_OFFSET 0xa2
#define SSD133X_SET_DISPLAY_NORMAL 0xa4
#define SSD133X_SET_MASTER_CONFIG 0xad
#define SSD133X_POWER_SAVE_MODE 0xb0
#define SSD133X_PHASES_PERIOD 0xb1
#define SSD133X_SET_CLOCK_FREQ 0xb3
#define SSD133X_SET_PRECHARGE_VOLTAGE 0xbb
#define SSD133X_SET_VCOMH_VOLTAGE 0xbe
#define MAX_CONTRAST 255
const struct ssd130x_deviceinfo ssd130x_variants[] = {
@ -180,6 +200,12 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = {
.default_width = 128,
.default_height = 128,
.family_id = SSD132X_FAMILY,
},
/* ssd133x family */
[SSD1331_ID] = {
.default_width = 96,
.default_height = 64,
.family_id = SSD133X_FAMILY,
}
};
EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X);
@ -589,6 +615,117 @@ static int ssd132x_init(struct ssd130x_device *ssd130x)
return 0;
}
static int ssd133x_init(struct ssd130x_device *ssd130x)
{
int ret;
/* Set color A contrast */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_CONTRAST_A, 0x91);
if (ret < 0)
return ret;
/* Set color B contrast */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_CONTRAST_B, 0x50);
if (ret < 0)
return ret;
/* Set color C contrast */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_CONTRAST_C, 0x7d);
if (ret < 0)
return ret;
/* Set master current */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_MASTER_CURRENT, 0x06);
if (ret < 0)
return ret;
/* Set column start and end */
ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_COL_RANGE, 0x00, ssd130x->width - 1);
if (ret < 0)
return ret;
/* Set row start and end */
ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_ROW_RANGE, 0x00, ssd130x->height - 1);
if (ret < 0)
return ret;
/*
* Horizontal Address Increment
* Normal order SA,SB,SC (e.g. RGB)
* COM Split Odd Even
* 256 color format
*/
ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_SEG_REMAP, 0x20);
if (ret < 0)
return ret;
/* Set display start and offset */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_DISPLAY_START, 0x00);
if (ret < 0)
return ret;
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_DISPLAY_OFFSET, 0x00);
if (ret < 0)
return ret;
/* Set display mode normal */
ret = ssd130x_write_cmd(ssd130x, 1, SSD133X_SET_DISPLAY_NORMAL);
if (ret < 0)
return ret;
/* Set multiplex ratio value */
ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1);
if (ret < 0)
return ret;
/* Set master configuration */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_MASTER_CONFIG, 0x8e);
if (ret < 0)
return ret;
/* Set power mode */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_POWER_SAVE_MODE, 0x0b);
if (ret < 0)
return ret;
/* Set Phase 1 and 2 period */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_PHASES_PERIOD, 0x31);
if (ret < 0)
return ret;
/* Set clock divider */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_CLOCK_FREQ, 0xf0);
if (ret < 0)
return ret;
/* Set pre-charge A */
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_A, 0x64);
if (ret < 0)
return ret;
/* Set pre-charge B */
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_B, 0x78);
if (ret < 0)
return ret;
/* Set pre-charge C */
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_C, 0x64);
if (ret < 0)
return ret;
/* Set pre-charge level */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_PRECHARGE_VOLTAGE, 0x3a);
if (ret < 0)
return ret;
/* Set VCOMH voltage */
ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_VCOMH_VOLTAGE, 0x3e);
if (ret < 0)
return ret;
return 0;
}
static int ssd130x_update_rect(struct ssd130x_device *ssd130x,
struct drm_rect *rect, u8 *buf,
u8 *data_array)
@ -753,6 +890,47 @@ static int ssd132x_update_rect(struct ssd130x_device *ssd130x,
return ret;
}
static int ssd133x_update_rect(struct ssd130x_device *ssd130x,
struct drm_rect *rect, u8 *data_array,
unsigned int pitch)
{
unsigned int x = rect->x1;
unsigned int y = rect->y1;
unsigned int columns = drm_rect_width(rect);
unsigned int rows = drm_rect_height(rect);
int ret;
/*
* The screen is divided in Segment and Common outputs, where
* COM0 to COM[N - 1] are the rows and SEG0 to SEG[M - 1] are
* the columns.
*
* Each Segment has a 8-bit pixel and each Common output has a
* row of pixels. When using the (default) horizontal address
* increment mode, each byte of data sent to the controller has
* a Segment (e.g: SEG0).
*
* When using the 256 color depth format, each pixel contains 3
* sub-pixels for color A, B and C. These have 3 bit, 3 bit and
* 2 bits respectively.
*/
/* Set column start and end */
ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_COL_RANGE, x, columns - 1);
if (ret < 0)
return ret;
/* Set row start and end */
ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_ROW_RANGE, y, rows - 1);
if (ret < 0)
return ret;
/* Write out update in one go since horizontal addressing mode is used */
ret = ssd130x_write_data(ssd130x, data_array, pitch * rows);
return ret;
}
static void ssd130x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array)
{
unsigned int pages = DIV_ROUND_UP(ssd130x->height, SSD130X_PAGE_HEIGHT);
@ -805,6 +983,22 @@ static void ssd132x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array)
ssd130x_write_data(ssd130x, data_array, columns * height);
}
static void ssd133x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array)
{
const struct drm_format_info *fi = drm_format_info(DRM_FORMAT_RGB332);
unsigned int pitch;
if (!fi)
return;
pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width);
memset(data_array, 0, pitch * ssd130x->height);
/* Write out update in one go since horizontal addressing mode is used */
ssd130x_write_data(ssd130x, data_array, pitch * ssd130x->height);
}
static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb,
const struct iosys_map *vmap,
struct drm_rect *rect,
@ -866,6 +1060,36 @@ static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb,
return ret;
}
static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb,
const struct iosys_map *vmap,
struct drm_rect *rect, u8 *data_array,
struct drm_format_conv_state *fmtcnv_state)
{
struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev);
const struct drm_format_info *fi = drm_format_info(DRM_FORMAT_RGB332);
unsigned int dst_pitch;
struct iosys_map dst;
int ret = 0;
if (!fi)
return -EINVAL;
dst_pitch = drm_format_info_min_pitch(fi, 0, drm_rect_width(rect));
ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
if (ret)
return ret;
iosys_map_set_vaddr(&dst, data_array);
drm_fb_xrgb8888_to_rgb332(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
ssd133x_update_rect(ssd130x, rect, data_array, dst_pitch);
return ret;
}
static int ssd130x_primary_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
@ -964,6 +1188,29 @@ static int ssd132x_primary_plane_atomic_check(struct drm_plane *plane,
return 0;
}
static int ssd133x_primary_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_crtc *crtc = plane_state->crtc;
struct drm_crtc_state *crtc_state = NULL;
int ret;
if (crtc)
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
false, false);
if (ret)
return ret;
else if (!plane_state->visible)
return 0;
return 0;
}
static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
@ -1034,6 +1281,39 @@ static void ssd132x_primary_plane_atomic_update(struct drm_plane *plane,
drm_dev_exit(idx);
}
static void ssd133x_primary_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc_state);
struct drm_framebuffer *fb = plane_state->fb;
struct drm_atomic_helper_damage_iter iter;
struct drm_device *drm = plane->dev;
struct drm_rect dst_clip;
struct drm_rect damage;
int idx;
if (!drm_dev_enter(drm, &idx))
return;
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
dst_clip = plane_state->dst;
if (!drm_rect_intersect(&dst_clip, &damage))
continue;
ssd133x_fb_blit_rect(fb, &shadow_plane_state->data[0], &dst_clip,
ssd130x_crtc_state->data_array,
&shadow_plane_state->fmtcnv_state);
}
drm_dev_exit(idx);
}
static void ssd130x_primary_plane_atomic_disable(struct drm_plane *plane,
struct drm_atomic_state *state)
{
@ -1082,6 +1362,30 @@ static void ssd132x_primary_plane_atomic_disable(struct drm_plane *plane,
drm_dev_exit(idx);
}
static void ssd133x_primary_plane_atomic_disable(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_device *drm = plane->dev;
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_crtc_state *crtc_state;
struct ssd130x_crtc_state *ssd130x_crtc_state;
int idx;
if (!plane_state->crtc)
return;
crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
ssd130x_crtc_state = to_ssd130x_crtc_state(crtc_state);
if (!drm_dev_enter(drm, &idx))
return;
ssd133x_clear_screen(ssd130x, ssd130x_crtc_state->data_array);
drm_dev_exit(idx);
}
/* Called during init to allocate the plane's atomic state. */
static void ssd130x_primary_plane_reset(struct drm_plane *plane)
{
@ -1144,6 +1448,12 @@ static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs[]
.atomic_check = ssd132x_primary_plane_atomic_check,
.atomic_update = ssd132x_primary_plane_atomic_update,
.atomic_disable = ssd132x_primary_plane_atomic_disable,
},
[SSD133X_FAMILY] = {
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
.atomic_check = ssd133x_primary_plane_atomic_check,
.atomic_update = ssd133x_primary_plane_atomic_update,
.atomic_disable = ssd133x_primary_plane_atomic_disable,
}
};
@ -1214,6 +1524,33 @@ static int ssd132x_crtc_atomic_check(struct drm_crtc *crtc,
return 0;
}
static int ssd133x_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_device *drm = crtc->dev;
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
struct ssd130x_crtc_state *ssd130x_state = to_ssd130x_crtc_state(crtc_state);
const struct drm_format_info *fi = drm_format_info(DRM_FORMAT_RGB332);
unsigned int pitch;
int ret;
if (!fi)
return -EINVAL;
ret = drm_crtc_helper_atomic_check(crtc, state);
if (ret)
return ret;
pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width);
ssd130x_state->data_array = kmalloc(pitch * ssd130x->height, GFP_KERNEL);
if (!ssd130x_state->data_array)
return -ENOMEM;
return 0;
}
/* Called during init to allocate the CRTC's atomic state. */
static void ssd130x_crtc_reset(struct drm_crtc *crtc)
{
@ -1275,6 +1612,10 @@ static const struct drm_crtc_helper_funcs ssd130x_crtc_helper_funcs[] = {
.mode_valid = ssd130x_crtc_mode_valid,
.atomic_check = ssd132x_crtc_atomic_check,
},
[SSD133X_FAMILY] = {
.mode_valid = ssd130x_crtc_mode_valid,
.atomic_check = ssd133x_crtc_atomic_check,
},
};
static const struct drm_crtc_funcs ssd130x_crtc_funcs = {
@ -1337,6 +1678,31 @@ power_off:
ssd130x_power_off(ssd130x);
}
static void ssd133x_encoder_atomic_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_device *drm = encoder->dev;
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
int ret;
ret = ssd130x_power_on(ssd130x);
if (ret)
return;
ret = ssd133x_init(ssd130x);
if (ret)
goto power_off;
ssd130x_write_cmd(ssd130x, 1, SSD13XX_DISPLAY_ON);
backlight_enable(ssd130x->bl_dev);
return;
power_off:
ssd130x_power_off(ssd130x);
}
static void ssd130x_encoder_atomic_disable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
@ -1358,6 +1724,10 @@ static const struct drm_encoder_helper_funcs ssd130x_encoder_helper_funcs[] = {
[SSD132X_FAMILY] = {
.atomic_enable = ssd132x_encoder_atomic_enable,
.atomic_disable = ssd130x_encoder_atomic_disable,
},
[SSD133X_FAMILY] = {
.atomic_enable = ssd133x_encoder_atomic_enable,
.atomic_disable = ssd130x_encoder_atomic_disable,
}
};

View File

@ -25,7 +25,8 @@
enum ssd130x_family_ids {
SSD130X_FAMILY,
SSD132X_FAMILY
SSD132X_FAMILY,
SSD133X_FAMILY
};
enum ssd130x_variants {
@ -39,6 +40,8 @@ enum ssd130x_variants {
SSD1322_ID,
SSD1325_ID,
SSD1327_ID,
/* ssd133x family */
SSD1331_ID,
NR_SSD130X_VARIANTS
};

View File

@ -522,7 +522,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
if (err < 0) {
dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n",
dpaux->irq, err);
return err;
goto err_pm_disable;
}
disable_irq(dpaux->irq);
@ -542,7 +542,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
*/
err = tegra_dpaux_pad_config(dpaux, DPAUX_PADCTL_FUNC_I2C);
if (err < 0)
return err;
goto err_pm_disable;
#ifdef CONFIG_GENERIC_PINCONF
dpaux->desc.name = dev_name(&pdev->dev);
@ -555,7 +555,8 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
dpaux->pinctrl = devm_pinctrl_register(&pdev->dev, &dpaux->desc, dpaux);
if (IS_ERR(dpaux->pinctrl)) {
dev_err(&pdev->dev, "failed to register pincontrol\n");
return PTR_ERR(dpaux->pinctrl);
err = PTR_ERR(dpaux->pinctrl);
goto err_pm_disable;
}
#endif
/* enable and clear all interrupts */
@ -571,10 +572,15 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
err = devm_of_dp_aux_populate_ep_devices(&dpaux->aux);
if (err < 0) {
dev_err(dpaux->dev, "failed to populate AUX bus: %d\n", err);
return err;
goto err_pm_disable;
}
return 0;
err_pm_disable:
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return err;
}
static void tegra_dpaux_remove(struct platform_device *pdev)

View File

@ -13,7 +13,6 @@
#include <drm/drm_atomic.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_fixed.h>
#include <drm/drm_probe_helper.h>
@ -26,6 +25,7 @@
/* XXX move to include/uapi/drm/drm_fourcc.h? */
#define DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT BIT_ULL(22)
struct edid;
struct reset_control;
struct tegra_drm {

View File

@ -1544,9 +1544,11 @@ static int tegra_dsi_ganged_probe(struct tegra_dsi *dsi)
np = of_parse_phandle(dsi->dev->of_node, "nvidia,ganged-mode", 0);
if (np) {
struct platform_device *gangster = of_find_device_by_node(np);
of_node_put(np);
if (!gangster)
return -EPROBE_DEFER;
dsi->slave = platform_get_drvdata(gangster);
of_node_put(np);
if (!dsi->slave) {
put_device(&gangster->dev);
@ -1594,44 +1596,58 @@ static int tegra_dsi_probe(struct platform_device *pdev)
if (!pdev->dev.pm_domain) {
dsi->rst = devm_reset_control_get(&pdev->dev, "dsi");
if (IS_ERR(dsi->rst))
return PTR_ERR(dsi->rst);
if (IS_ERR(dsi->rst)) {
err = PTR_ERR(dsi->rst);
goto remove;
}
}
dsi->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dsi->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk),
"cannot get DSI clock\n");
if (IS_ERR(dsi->clk)) {
err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk),
"cannot get DSI clock\n");
goto remove;
}
dsi->clk_lp = devm_clk_get(&pdev->dev, "lp");
if (IS_ERR(dsi->clk_lp))
return dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk_lp),
"cannot get low-power clock\n");
if (IS_ERR(dsi->clk_lp)) {
err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk_lp),
"cannot get low-power clock\n");
goto remove;
}
dsi->clk_parent = devm_clk_get(&pdev->dev, "parent");
if (IS_ERR(dsi->clk_parent))
return dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk_parent),
"cannot get parent clock\n");
if (IS_ERR(dsi->clk_parent)) {
err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk_parent),
"cannot get parent clock\n");
goto remove;
}
dsi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");
if (IS_ERR(dsi->vdd))
return dev_err_probe(&pdev->dev, PTR_ERR(dsi->vdd),
"cannot get VDD supply\n");
if (IS_ERR(dsi->vdd)) {
err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->vdd),
"cannot get VDD supply\n");
goto remove;
}
err = tegra_dsi_setup_clocks(dsi);
if (err < 0) {
dev_err(&pdev->dev, "cannot setup clocks\n");
return err;
goto remove;
}
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dsi->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(dsi->regs))
return PTR_ERR(dsi->regs);
if (IS_ERR(dsi->regs)) {
err = PTR_ERR(dsi->regs);
goto remove;
}
dsi->mipi = tegra_mipi_request(&pdev->dev, pdev->dev.of_node);
if (IS_ERR(dsi->mipi))
return PTR_ERR(dsi->mipi);
if (IS_ERR(dsi->mipi)) {
err = PTR_ERR(dsi->mipi);
goto remove;
}
dsi->host.ops = &tegra_dsi_host_ops;
dsi->host.dev = &pdev->dev;
@ -1659,9 +1675,12 @@ static int tegra_dsi_probe(struct platform_device *pdev)
return 0;
unregister:
pm_runtime_disable(&pdev->dev);
mipi_dsi_host_unregister(&dsi->host);
mipi_free:
tegra_mipi_free(dsi->mipi);
remove:
tegra_output_remove(&dsi->output);
return err;
}

View File

@ -1856,12 +1856,14 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
return err;
hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hdmi->regs))
return PTR_ERR(hdmi->regs);
if (IS_ERR(hdmi->regs)) {
err = PTR_ERR(hdmi->regs);
goto remove;
}
err = platform_get_irq(pdev, 0);
if (err < 0)
return err;
goto remove;
hdmi->irq = err;
@ -1870,18 +1872,18 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n",
hdmi->irq, err);
return err;
goto remove;
}
platform_set_drvdata(pdev, hdmi);
err = devm_pm_runtime_enable(&pdev->dev);
if (err)
return err;
goto remove;
err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (err)
return err;
goto remove;
INIT_LIST_HEAD(&hdmi->client.list);
hdmi->client.ops = &hdmi_client_ops;
@ -1891,10 +1893,14 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
return err;
goto remove;
}
return 0;
remove:
tegra_output_remove(&hdmi->output);
return err;
}
static void tegra_hdmi_remove(struct platform_device *pdev)

View File

@ -8,6 +8,7 @@
#include <linux/of.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_simple_kms_helper.h>
@ -142,8 +143,10 @@ int tegra_output_probe(struct tegra_output *output)
GPIOD_IN,
"HDMI hotplug detect");
if (IS_ERR(output->hpd_gpio)) {
if (PTR_ERR(output->hpd_gpio) != -ENOENT)
return PTR_ERR(output->hpd_gpio);
if (PTR_ERR(output->hpd_gpio) != -ENOENT) {
err = PTR_ERR(output->hpd_gpio);
goto put_i2c;
}
output->hpd_gpio = NULL;
}
@ -152,7 +155,7 @@ int tegra_output_probe(struct tegra_output *output)
err = gpiod_to_irq(output->hpd_gpio);
if (err < 0) {
dev_err(output->dev, "gpiod_to_irq(): %d\n", err);
return err;
goto put_i2c;
}
output->hpd_irq = err;
@ -165,7 +168,7 @@ int tegra_output_probe(struct tegra_output *output)
if (err < 0) {
dev_err(output->dev, "failed to request IRQ#%u: %d\n",
output->hpd_irq, err);
return err;
goto put_i2c;
}
output->connector.polled = DRM_CONNECTOR_POLL_HPD;
@ -179,6 +182,12 @@ int tegra_output_probe(struct tegra_output *output)
}
return 0;
put_i2c:
if (output->ddc)
i2c_put_adapter(output->ddc);
return err;
}
void tegra_output_remove(struct tegra_output *output)

View File

@ -225,26 +225,28 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
rgb->clk = devm_clk_get(dc->dev, NULL);
if (IS_ERR(rgb->clk)) {
dev_err(dc->dev, "failed to get clock\n");
return PTR_ERR(rgb->clk);
err = PTR_ERR(rgb->clk);
goto remove;
}
rgb->clk_parent = devm_clk_get(dc->dev, "parent");
if (IS_ERR(rgb->clk_parent)) {
dev_err(dc->dev, "failed to get parent clock\n");
return PTR_ERR(rgb->clk_parent);
err = PTR_ERR(rgb->clk_parent);
goto remove;
}
err = clk_set_parent(rgb->clk, rgb->clk_parent);
if (err < 0) {
dev_err(dc->dev, "failed to set parent clock: %d\n", err);
return err;
goto remove;
}
rgb->pll_d_out0 = clk_get_sys(NULL, "pll_d_out0");
if (IS_ERR(rgb->pll_d_out0)) {
err = PTR_ERR(rgb->pll_d_out0);
dev_err(dc->dev, "failed to get pll_d_out0: %d\n", err);
return err;
goto remove;
}
if (dc->soc->has_pll_d2_out0) {
@ -252,13 +254,19 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
if (IS_ERR(rgb->pll_d2_out0)) {
err = PTR_ERR(rgb->pll_d2_out0);
dev_err(dc->dev, "failed to get pll_d2_out0: %d\n", err);
return err;
goto put_pll;
}
}
dc->rgb = &rgb->output;
return 0;
put_pll:
clk_put(rgb->pll_d_out0);
remove:
tegra_output_remove(&rgb->output);
return err;
}
void tegra_dc_rgb_remove(struct tegra_dc *dc)

View File

@ -20,6 +20,7 @@
#include <drm/display/drm_scdc_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_edid.h>
#include <drm/drm_eld.h>
#include <drm/drm_file.h>
#include <drm/drm_panel.h>

View File

@ -182,9 +182,6 @@ static void tilcdc_fini(struct drm_device *dev)
if (priv->clk)
clk_put(priv->clk);
if (priv->mmio)
iounmap(priv->mmio);
if (priv->wq)
destroy_workqueue(priv->wq);
@ -201,7 +198,6 @@ static int tilcdc_init(const struct drm_driver *ddrv, struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct device_node *node = dev->of_node;
struct tilcdc_drm_private *priv;
struct resource *res;
u32 bpp = 0;
int ret;
@ -226,17 +222,10 @@ static int tilcdc_init(const struct drm_driver *ddrv, struct device *dev)
goto init_failed;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "failed to get memory resource\n");
ret = -EINVAL;
goto init_failed;
}
priv->mmio = ioremap(res->start, resource_size(res));
if (!priv->mmio) {
dev_err(dev, "failed to ioremap\n");
ret = -ENOMEM;
priv->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->mmio)) {
dev_err(dev, "failed to request / ioremap\n");
ret = PTR_ERR(priv->mmio);
goto init_failed;
}

View File

@ -3,4 +3,7 @@
obj-$(CONFIG_DRM_TTM_KUNIT_TEST) += \
ttm_device_test.o \
ttm_pool_test.o \
ttm_resource_test.o \
ttm_tt_test.o \
ttm_bo_test.o \
ttm_kunit_helpers.o

View File

@ -0,0 +1,622 @@
// SPDX-License-Identifier: GPL-2.0 AND MIT
/*
* Copyright © 2023 Intel Corporation
*/
#include <linux/dma-resv.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/mutex.h>
#include <linux/ww_mutex.h>
#include <drm/ttm/ttm_resource.h>
#include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_tt.h>
#include "ttm_kunit_helpers.h"
#define BO_SIZE SZ_8K
struct ttm_bo_test_case {
const char *description;
bool interruptible;
bool no_wait;
};
static const struct ttm_bo_test_case ttm_bo_reserved_cases[] = {
{
.description = "Cannot be interrupted and sleeps",
.interruptible = false,
.no_wait = false,
},
{
.description = "Cannot be interrupted, locks straight away",
.interruptible = false,
.no_wait = true,
},
{
.description = "Can be interrupted, sleeps",
.interruptible = true,
.no_wait = false,
},
};
static void ttm_bo_init_case_desc(const struct ttm_bo_test_case *t,
char *desc)
{
strscpy(desc, t->description, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(ttm_bo_reserve, ttm_bo_reserved_cases, ttm_bo_init_case_desc);
static void ttm_bo_reserve_optimistic_no_ticket(struct kunit *test)
{
const struct ttm_bo_test_case *params = test->param_value;
struct ttm_buffer_object *bo;
int err;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
err = ttm_bo_reserve(bo, params->interruptible, params->no_wait, NULL);
KUNIT_ASSERT_EQ(test, err, 0);
dma_resv_unlock(bo->base.resv);
}
static void ttm_bo_reserve_locked_no_sleep(struct kunit *test)
{
struct ttm_buffer_object *bo;
bool interruptible = false;
bool no_wait = true;
int err;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
/* Let's lock it beforehand */
dma_resv_lock(bo->base.resv, NULL);
err = ttm_bo_reserve(bo, interruptible, no_wait, NULL);
dma_resv_unlock(bo->base.resv);
KUNIT_ASSERT_EQ(test, err, -EBUSY);
}
static void ttm_bo_reserve_no_wait_ticket(struct kunit *test)
{
struct ttm_buffer_object *bo;
struct ww_acquire_ctx ctx;
bool interruptible = false;
bool no_wait = true;
int err;
ww_acquire_init(&ctx, &reservation_ww_class);
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
err = ttm_bo_reserve(bo, interruptible, no_wait, &ctx);
KUNIT_ASSERT_EQ(test, err, -EBUSY);
ww_acquire_fini(&ctx);
}
static void ttm_bo_reserve_double_resv(struct kunit *test)
{
struct ttm_buffer_object *bo;
struct ww_acquire_ctx ctx;
bool interruptible = false;
bool no_wait = false;
int err;
ww_acquire_init(&ctx, &reservation_ww_class);
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
err = ttm_bo_reserve(bo, interruptible, no_wait, &ctx);
KUNIT_ASSERT_EQ(test, err, 0);
err = ttm_bo_reserve(bo, interruptible, no_wait, &ctx);
dma_resv_unlock(bo->base.resv);
ww_acquire_fini(&ctx);
KUNIT_ASSERT_EQ(test, err, -EALREADY);
}
/*
* A test case heavily inspired by ww_test_edeadlk_normal(). It injects
* a deadlock by manipulating the sequence number of the context that holds
* dma_resv lock of bo2 so the other context is "wounded" and has to back off
* (indicated by -EDEADLK). The subtest checks if ttm_bo_reserve() properly
* propagates that error.
*/
static void ttm_bo_reserve_deadlock(struct kunit *test)
{
struct ttm_buffer_object *bo1, *bo2;
struct ww_acquire_ctx ctx1, ctx2;
bool interruptible = false;
bool no_wait = false;
int err;
bo1 = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
bo2 = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
ww_acquire_init(&ctx1, &reservation_ww_class);
mutex_lock(&bo2->base.resv->lock.base);
/* The deadlock will be caught by WW mutex, don't warn about it */
lock_release(&bo2->base.resv->lock.base.dep_map, 1);
bo2->base.resv->lock.ctx = &ctx2;
ctx2 = ctx1;
ctx2.stamp--; /* Make the context holding the lock younger */
err = ttm_bo_reserve(bo1, interruptible, no_wait, &ctx1);
KUNIT_ASSERT_EQ(test, err, 0);
err = ttm_bo_reserve(bo2, interruptible, no_wait, &ctx1);
KUNIT_ASSERT_EQ(test, err, -EDEADLK);
dma_resv_unlock(bo1->base.resv);
ww_acquire_fini(&ctx1);
}
#if IS_BUILTIN(CONFIG_DRM_TTM_KUNIT_TEST)
struct signal_timer {
struct timer_list timer;
struct ww_acquire_ctx *ctx;
};
static void signal_for_ttm_bo_reserve(struct timer_list *t)
{
struct signal_timer *s_timer = from_timer(s_timer, t, timer);
struct task_struct *task = s_timer->ctx->task;
do_send_sig_info(SIGTERM, SEND_SIG_PRIV, task, PIDTYPE_PID);
}
static int threaded_ttm_bo_reserve(void *arg)
{
struct ttm_buffer_object *bo = arg;
struct signal_timer s_timer;
struct ww_acquire_ctx ctx;
bool interruptible = true;
bool no_wait = false;
int err;
ww_acquire_init(&ctx, &reservation_ww_class);
/* Prepare a signal that will interrupt the reservation attempt */
timer_setup_on_stack(&s_timer.timer, &signal_for_ttm_bo_reserve, 0);
s_timer.ctx = &ctx;
mod_timer(&s_timer.timer, msecs_to_jiffies(100));
err = ttm_bo_reserve(bo, interruptible, no_wait, &ctx);
timer_delete_sync(&s_timer.timer);
destroy_timer_on_stack(&s_timer.timer);
ww_acquire_fini(&ctx);
return err;
}
static void ttm_bo_reserve_interrupted(struct kunit *test)
{
struct ttm_buffer_object *bo;
struct task_struct *task;
int err;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
task = kthread_create(threaded_ttm_bo_reserve, bo, "ttm-bo-reserve");
if (IS_ERR(task))
KUNIT_FAIL(test, "Couldn't create ttm bo reserve task\n");
/* Take a lock so the threaded reserve has to wait */
mutex_lock(&bo->base.resv->lock.base);
wake_up_process(task);
msleep(20);
err = kthread_stop(task);
mutex_unlock(&bo->base.resv->lock.base);
KUNIT_ASSERT_EQ(test, err, -ERESTARTSYS);
}
#endif /* IS_BUILTIN(CONFIG_DRM_TTM_KUNIT_TEST) */
static void ttm_bo_unreserve_basic(struct kunit *test)
{
struct ttm_test_devices *priv = test->priv;
struct ttm_buffer_object *bo;
struct ttm_device *ttm_dev;
struct ttm_resource *res1, *res2;
struct ttm_place *place;
struct ttm_resource_manager *man;
unsigned int bo_prio = TTM_MAX_BO_PRIORITY - 1;
uint32_t mem_type = TTM_PL_SYSTEM;
int err;
place = ttm_place_kunit_init(test, mem_type, 0);
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
err = ttm_device_kunit_init(priv, ttm_dev, false, false);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
bo->priority = bo_prio;
err = ttm_resource_alloc(bo, place, &res1);
KUNIT_ASSERT_EQ(test, err, 0);
bo->resource = res1;
/* Add a dummy resource to populate LRU */
ttm_resource_alloc(bo, place, &res2);
dma_resv_lock(bo->base.resv, NULL);
ttm_bo_unreserve(bo);
man = ttm_manager_type(priv->ttm_dev, mem_type);
KUNIT_ASSERT_EQ(test,
list_is_last(&res1->lru, &man->lru[bo->priority]), 1);
ttm_resource_free(bo, &res2);
ttm_resource_free(bo, &res1);
}
static void ttm_bo_unreserve_pinned(struct kunit *test)
{
struct ttm_test_devices *priv = test->priv;
struct ttm_buffer_object *bo;
struct ttm_device *ttm_dev;
struct ttm_resource *res1, *res2;
struct ttm_place *place;
uint32_t mem_type = TTM_PL_SYSTEM;
int err;
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
err = ttm_device_kunit_init(priv, ttm_dev, false, false);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
place = ttm_place_kunit_init(test, mem_type, 0);
dma_resv_lock(bo->base.resv, NULL);
ttm_bo_pin(bo);
err = ttm_resource_alloc(bo, place, &res1);
KUNIT_ASSERT_EQ(test, err, 0);
bo->resource = res1;
/* Add a dummy resource to the pinned list */
err = ttm_resource_alloc(bo, place, &res2);
KUNIT_ASSERT_EQ(test, err, 0);
KUNIT_ASSERT_EQ(test,
list_is_last(&res2->lru, &priv->ttm_dev->pinned), 1);
ttm_bo_unreserve(bo);
KUNIT_ASSERT_EQ(test,
list_is_last(&res1->lru, &priv->ttm_dev->pinned), 1);
ttm_resource_free(bo, &res1);
ttm_resource_free(bo, &res2);
}
static void ttm_bo_unreserve_bulk(struct kunit *test)
{
struct ttm_test_devices *priv = test->priv;
struct ttm_lru_bulk_move lru_bulk_move;
struct ttm_lru_bulk_move_pos *pos;
struct ttm_buffer_object *bo1, *bo2;
struct ttm_resource *res1, *res2;
struct ttm_device *ttm_dev;
struct ttm_place *place;
uint32_t mem_type = TTM_PL_SYSTEM;
unsigned int bo_priority = 0;
int err;
ttm_lru_bulk_move_init(&lru_bulk_move);
place = ttm_place_kunit_init(test, mem_type, 0);
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
err = ttm_device_kunit_init(priv, ttm_dev, false, false);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
bo1 = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
bo2 = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
dma_resv_lock(bo1->base.resv, NULL);
ttm_bo_set_bulk_move(bo1, &lru_bulk_move);
dma_resv_unlock(bo1->base.resv);
err = ttm_resource_alloc(bo1, place, &res1);
KUNIT_ASSERT_EQ(test, err, 0);
bo1->resource = res1;
dma_resv_lock(bo2->base.resv, NULL);
ttm_bo_set_bulk_move(bo2, &lru_bulk_move);
dma_resv_unlock(bo2->base.resv);
err = ttm_resource_alloc(bo2, place, &res2);
KUNIT_ASSERT_EQ(test, err, 0);
bo2->resource = res2;
ttm_bo_reserve(bo1, false, false, NULL);
ttm_bo_unreserve(bo1);
pos = &lru_bulk_move.pos[mem_type][bo_priority];
KUNIT_ASSERT_PTR_EQ(test, res1, pos->last);
ttm_resource_free(bo1, &res1);
ttm_resource_free(bo2, &res2);
}
static void ttm_bo_put_basic(struct kunit *test)
{
struct ttm_test_devices *priv = test->priv;
struct ttm_buffer_object *bo;
struct ttm_resource *res;
struct ttm_device *ttm_dev;
struct ttm_place *place;
uint32_t mem_type = TTM_PL_SYSTEM;
int err;
place = ttm_place_kunit_init(test, mem_type, 0);
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
err = ttm_device_kunit_init(priv, ttm_dev, false, false);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
bo->type = ttm_bo_type_device;
err = ttm_resource_alloc(bo, place, &res);
KUNIT_ASSERT_EQ(test, err, 0);
bo->resource = res;
dma_resv_lock(bo->base.resv, NULL);
err = ttm_tt_create(bo, false);
dma_resv_unlock(bo->base.resv);
KUNIT_EXPECT_EQ(test, err, 0);
ttm_bo_put(bo);
}
static const char *mock_name(struct dma_fence *f)
{
return "kunit-ttm-bo-put";
}
static const struct dma_fence_ops mock_fence_ops = {
.get_driver_name = mock_name,
.get_timeline_name = mock_name,
};
static void ttm_bo_put_shared_resv(struct kunit *test)
{
struct ttm_test_devices *priv = test->priv;
struct ttm_buffer_object *bo;
struct dma_resv *external_resv;
struct dma_fence *fence;
/* A dummy DMA fence lock */
spinlock_t fence_lock;
struct ttm_device *ttm_dev;
int err;
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
err = ttm_device_kunit_init(priv, ttm_dev, false, false);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
external_resv = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, external_resv);
dma_resv_init(external_resv);
fence = kunit_kzalloc(test, sizeof(*fence), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, fence);
spin_lock_init(&fence_lock);
dma_fence_init(fence, &mock_fence_ops, &fence_lock, 0, 0);
dma_resv_lock(external_resv, NULL);
dma_resv_reserve_fences(external_resv, 1);
dma_resv_add_fence(external_resv, fence, DMA_RESV_USAGE_BOOKKEEP);
dma_resv_unlock(external_resv);
dma_fence_signal(fence);
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
bo->type = ttm_bo_type_device;
bo->base.resv = external_resv;
ttm_bo_put(bo);
}
static void ttm_bo_pin_basic(struct kunit *test)
{
struct ttm_test_devices *priv = test->priv;
struct ttm_buffer_object *bo;
struct ttm_device *ttm_dev;
unsigned int no_pins = 3;
int err;
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
err = ttm_device_kunit_init(priv, ttm_dev, false, false);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
for (int i = 0; i < no_pins; i++) {
dma_resv_lock(bo->base.resv, NULL);
ttm_bo_pin(bo);
dma_resv_unlock(bo->base.resv);
}
KUNIT_ASSERT_EQ(test, bo->pin_count, no_pins);
}
static void ttm_bo_pin_unpin_resource(struct kunit *test)
{
struct ttm_test_devices *priv = test->priv;
struct ttm_lru_bulk_move lru_bulk_move;
struct ttm_lru_bulk_move_pos *pos;
struct ttm_buffer_object *bo;
struct ttm_resource *res;
struct ttm_device *ttm_dev;
struct ttm_place *place;
uint32_t mem_type = TTM_PL_SYSTEM;
unsigned int bo_priority = 0;
int err;
ttm_lru_bulk_move_init(&lru_bulk_move);
place = ttm_place_kunit_init(test, mem_type, 0);
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
err = ttm_device_kunit_init(priv, ttm_dev, false, false);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
err = ttm_resource_alloc(bo, place, &res);
KUNIT_ASSERT_EQ(test, err, 0);
bo->resource = res;
dma_resv_lock(bo->base.resv, NULL);
ttm_bo_set_bulk_move(bo, &lru_bulk_move);
ttm_bo_pin(bo);
dma_resv_unlock(bo->base.resv);
pos = &lru_bulk_move.pos[mem_type][bo_priority];
KUNIT_ASSERT_EQ(test, bo->pin_count, 1);
KUNIT_ASSERT_NULL(test, pos->first);
KUNIT_ASSERT_NULL(test, pos->last);
dma_resv_lock(bo->base.resv, NULL);
ttm_bo_unpin(bo);
dma_resv_unlock(bo->base.resv);
KUNIT_ASSERT_PTR_EQ(test, res, pos->last);
KUNIT_ASSERT_EQ(test, bo->pin_count, 0);
ttm_resource_free(bo, &res);
}
static void ttm_bo_multiple_pin_one_unpin(struct kunit *test)
{
struct ttm_test_devices *priv = test->priv;
struct ttm_lru_bulk_move lru_bulk_move;
struct ttm_lru_bulk_move_pos *pos;
struct ttm_buffer_object *bo;
struct ttm_resource *res;
struct ttm_device *ttm_dev;
struct ttm_place *place;
uint32_t mem_type = TTM_PL_SYSTEM;
unsigned int bo_priority = 0;
int err;
ttm_lru_bulk_move_init(&lru_bulk_move);
place = ttm_place_kunit_init(test, mem_type, 0);
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
err = ttm_device_kunit_init(priv, ttm_dev, false, false);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
err = ttm_resource_alloc(bo, place, &res);
KUNIT_ASSERT_EQ(test, err, 0);
bo->resource = res;
dma_resv_lock(bo->base.resv, NULL);
ttm_bo_set_bulk_move(bo, &lru_bulk_move);
/* Multiple pins */
ttm_bo_pin(bo);
ttm_bo_pin(bo);
dma_resv_unlock(bo->base.resv);
pos = &lru_bulk_move.pos[mem_type][bo_priority];
KUNIT_ASSERT_EQ(test, bo->pin_count, 2);
KUNIT_ASSERT_NULL(test, pos->first);
KUNIT_ASSERT_NULL(test, pos->last);
dma_resv_lock(bo->base.resv, NULL);
ttm_bo_unpin(bo);
dma_resv_unlock(bo->base.resv);
KUNIT_ASSERT_EQ(test, bo->pin_count, 1);
KUNIT_ASSERT_NULL(test, pos->first);
KUNIT_ASSERT_NULL(test, pos->last);
dma_resv_lock(bo->base.resv, NULL);
ttm_bo_unpin(bo);
dma_resv_unlock(bo->base.resv);
ttm_resource_free(bo, &res);
}
static struct kunit_case ttm_bo_test_cases[] = {
KUNIT_CASE_PARAM(ttm_bo_reserve_optimistic_no_ticket,
ttm_bo_reserve_gen_params),
KUNIT_CASE(ttm_bo_reserve_locked_no_sleep),
KUNIT_CASE(ttm_bo_reserve_no_wait_ticket),
KUNIT_CASE(ttm_bo_reserve_double_resv),
#if IS_BUILTIN(CONFIG_DRM_TTM_KUNIT_TEST)
KUNIT_CASE(ttm_bo_reserve_interrupted),
#endif
KUNIT_CASE(ttm_bo_reserve_deadlock),
KUNIT_CASE(ttm_bo_unreserve_basic),
KUNIT_CASE(ttm_bo_unreserve_pinned),
KUNIT_CASE(ttm_bo_unreserve_bulk),
KUNIT_CASE(ttm_bo_put_basic),
KUNIT_CASE(ttm_bo_put_shared_resv),
KUNIT_CASE(ttm_bo_pin_basic),
KUNIT_CASE(ttm_bo_pin_unpin_resource),
KUNIT_CASE(ttm_bo_multiple_pin_one_unpin),
{}
};
static struct kunit_suite ttm_bo_test_suite = {
.name = "ttm_bo",
.init = ttm_test_devices_init,
.exit = ttm_test_devices_fini,
.test_cases = ttm_bo_test_cases,
};
kunit_test_suites(&ttm_bo_test_suite);
MODULE_LICENSE("GPL");

View File

@ -2,9 +2,33 @@
/*
* Copyright © 2023 Intel Corporation
*/
#include <drm/ttm/ttm_tt.h>
#include "ttm_kunit_helpers.h"
static struct ttm_tt *ttm_tt_simple_create(struct ttm_buffer_object *bo,
uint32_t page_flags)
{
struct ttm_tt *tt;
tt = kzalloc(sizeof(*tt), GFP_KERNEL);
ttm_tt_init(tt, bo, page_flags, ttm_cached, 0);
return tt;
}
static void ttm_tt_simple_destroy(struct ttm_device *bdev, struct ttm_tt *ttm)
{
kfree(ttm);
}
static void dummy_ttm_bo_destroy(struct ttm_buffer_object *bo)
{
}
struct ttm_device_funcs ttm_dev_funcs = {
.ttm_tt_create = ttm_tt_simple_create,
.ttm_tt_destroy = ttm_tt_simple_destroy,
};
EXPORT_SYMBOL_GPL(ttm_dev_funcs);
@ -29,19 +53,41 @@ struct ttm_buffer_object *ttm_bo_kunit_init(struct kunit *test,
struct ttm_test_devices *devs,
size_t size)
{
struct drm_gem_object gem_obj = { .size = size };
struct drm_gem_object gem_obj = { };
struct ttm_buffer_object *bo;
int err;
bo = kunit_kzalloc(test, sizeof(*bo), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, bo);
bo->base = gem_obj;
err = drm_gem_object_init(devs->drm, &bo->base, size);
KUNIT_ASSERT_EQ(test, err, 0);
bo->bdev = devs->ttm_dev;
bo->destroy = dummy_ttm_bo_destroy;
kref_init(&bo->kref);
return bo;
}
EXPORT_SYMBOL_GPL(ttm_bo_kunit_init);
struct ttm_place *ttm_place_kunit_init(struct kunit *test,
uint32_t mem_type, uint32_t flags)
{
struct ttm_place *place;
place = kunit_kzalloc(test, sizeof(*place), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, place);
place->mem_type = mem_type;
place->flags = flags;
return place;
}
EXPORT_SYMBOL_GPL(ttm_place_kunit_init);
struct ttm_test_devices *ttm_test_devices_basic(struct kunit *test)
{
struct ttm_test_devices *devs;

View File

@ -8,6 +8,7 @@
#include <drm/drm_drv.h>
#include <drm/ttm/ttm_device.h>
#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_placement.h>
#include <drm/drm_kunit_helpers.h>
#include <kunit/test.h>
@ -28,6 +29,8 @@ int ttm_device_kunit_init(struct ttm_test_devices *priv,
struct ttm_buffer_object *ttm_bo_kunit_init(struct kunit *test,
struct ttm_test_devices *devs,
size_t size);
struct ttm_place *ttm_place_kunit_init(struct kunit *test,
uint32_t mem_type, uint32_t flags);
struct ttm_test_devices *ttm_test_devices_basic(struct kunit *test);
struct ttm_test_devices *ttm_test_devices_all(struct kunit *test);

View File

@ -78,10 +78,9 @@ static struct ttm_pool *ttm_pool_pre_populated(struct kunit *test,
struct ttm_test_devices *devs = priv->devs;
struct ttm_pool *pool;
struct ttm_tt *tt;
unsigned long order = __fls(size / PAGE_SIZE);
int err;
tt = ttm_tt_kunit_init(test, order, caching, size);
tt = ttm_tt_kunit_init(test, 0, caching, size);
KUNIT_ASSERT_NOT_NULL(test, tt);
pool = kunit_kzalloc(test, sizeof(*pool), GFP_KERNEL);

View File

@ -0,0 +1,335 @@
// SPDX-License-Identifier: GPL-2.0 AND MIT
/*
* Copyright © 2023 Intel Corporation
*/
#include <drm/ttm/ttm_resource.h>
#include "ttm_kunit_helpers.h"
#define RES_SIZE SZ_4K
#define TTM_PRIV_DUMMY_REG (TTM_NUM_MEM_TYPES - 1)
struct ttm_resource_test_case {
const char *description;
uint32_t mem_type;
uint32_t flags;
};
struct ttm_resource_test_priv {
struct ttm_test_devices *devs;
struct ttm_buffer_object *bo;
struct ttm_place *place;
};
static const struct ttm_resource_manager_func ttm_resource_manager_mock_funcs = { };
static int ttm_resource_test_init(struct kunit *test)
{
struct ttm_resource_test_priv *priv;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, priv);
priv->devs = ttm_test_devices_all(test);
KUNIT_ASSERT_NOT_NULL(test, priv->devs);
test->priv = priv;
return 0;
}
static void ttm_resource_test_fini(struct kunit *test)
{
struct ttm_resource_test_priv *priv = test->priv;
ttm_test_devices_put(test, priv->devs);
}
static void ttm_init_test_mocks(struct kunit *test,
struct ttm_resource_test_priv *priv,
uint32_t mem_type, uint32_t flags)
{
size_t size = RES_SIZE;
/* Make sure we have what we need for a good BO mock */
KUNIT_ASSERT_NOT_NULL(test, priv->devs->ttm_dev);
priv->bo = ttm_bo_kunit_init(test, priv->devs, size);
priv->place = ttm_place_kunit_init(test, mem_type, flags);
}
static void ttm_init_test_manager(struct kunit *test,
struct ttm_resource_test_priv *priv,
uint32_t mem_type)
{
struct ttm_device *ttm_dev = priv->devs->ttm_dev;
struct ttm_resource_manager *man;
size_t size = SZ_16K;
man = kunit_kzalloc(test, sizeof(*man), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, man);
man->use_tt = false;
man->func = &ttm_resource_manager_mock_funcs;
ttm_resource_manager_init(man, ttm_dev, size);
ttm_set_driver_manager(ttm_dev, mem_type, man);
ttm_resource_manager_set_used(man, true);
}
static const struct ttm_resource_test_case ttm_resource_cases[] = {
{
.description = "Init resource in TTM_PL_SYSTEM",
.mem_type = TTM_PL_SYSTEM,
},
{
.description = "Init resource in TTM_PL_VRAM",
.mem_type = TTM_PL_VRAM,
},
{
.description = "Init resource in a private placement",
.mem_type = TTM_PRIV_DUMMY_REG,
},
{
.description = "Init resource in TTM_PL_SYSTEM, set placement flags",
.mem_type = TTM_PL_SYSTEM,
.flags = TTM_PL_FLAG_TOPDOWN,
},
};
static void ttm_resource_case_desc(const struct ttm_resource_test_case *t, char *desc)
{
strscpy(desc, t->description, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(ttm_resource, ttm_resource_cases, ttm_resource_case_desc);
static void ttm_resource_init_basic(struct kunit *test)
{
const struct ttm_resource_test_case *params = test->param_value;
struct ttm_resource_test_priv *priv = test->priv;
struct ttm_resource *res;
struct ttm_buffer_object *bo;
struct ttm_place *place;
struct ttm_resource_manager *man;
uint64_t expected_usage;
ttm_init_test_mocks(test, priv, params->mem_type, params->flags);
bo = priv->bo;
place = priv->place;
if (params->mem_type > TTM_PL_SYSTEM)
ttm_init_test_manager(test, priv, params->mem_type);
res = kunit_kzalloc(test, sizeof(*res), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, res);
man = ttm_manager_type(priv->devs->ttm_dev, place->mem_type);
expected_usage = man->usage + RES_SIZE;
KUNIT_ASSERT_TRUE(test, list_empty(&man->lru[bo->priority]));
ttm_resource_init(bo, place, res);
KUNIT_ASSERT_EQ(test, res->start, 0);
KUNIT_ASSERT_EQ(test, res->size, RES_SIZE);
KUNIT_ASSERT_EQ(test, res->mem_type, place->mem_type);
KUNIT_ASSERT_EQ(test, res->placement, place->flags);
KUNIT_ASSERT_PTR_EQ(test, res->bo, bo);
KUNIT_ASSERT_NULL(test, res->bus.addr);
KUNIT_ASSERT_EQ(test, res->bus.offset, 0);
KUNIT_ASSERT_FALSE(test, res->bus.is_iomem);
KUNIT_ASSERT_EQ(test, res->bus.caching, ttm_cached);
KUNIT_ASSERT_EQ(test, man->usage, expected_usage);
KUNIT_ASSERT_TRUE(test, list_is_singular(&man->lru[bo->priority]));
ttm_resource_fini(man, res);
}
static void ttm_resource_init_pinned(struct kunit *test)
{
struct ttm_resource_test_priv *priv = test->priv;
struct ttm_resource *res;
struct ttm_buffer_object *bo;
struct ttm_place *place;
struct ttm_resource_manager *man;
ttm_init_test_mocks(test, priv, TTM_PL_SYSTEM, 0);
bo = priv->bo;
place = priv->place;
man = ttm_manager_type(priv->devs->ttm_dev, place->mem_type);
res = kunit_kzalloc(test, sizeof(*res), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, res);
KUNIT_ASSERT_TRUE(test, list_empty(&bo->bdev->pinned));
dma_resv_lock(bo->base.resv, NULL);
ttm_bo_pin(bo);
ttm_resource_init(bo, place, res);
KUNIT_ASSERT_TRUE(test, list_is_singular(&bo->bdev->pinned));
ttm_bo_unpin(bo);
ttm_resource_fini(man, res);
dma_resv_unlock(bo->base.resv);
KUNIT_ASSERT_TRUE(test, list_empty(&bo->bdev->pinned));
}
static void ttm_resource_fini_basic(struct kunit *test)
{
struct ttm_resource_test_priv *priv = test->priv;
struct ttm_resource *res;
struct ttm_buffer_object *bo;
struct ttm_place *place;
struct ttm_resource_manager *man;
ttm_init_test_mocks(test, priv, TTM_PL_SYSTEM, 0);
bo = priv->bo;
place = priv->place;
man = ttm_manager_type(priv->devs->ttm_dev, place->mem_type);
res = kunit_kzalloc(test, sizeof(*res), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, res);
ttm_resource_init(bo, place, res);
ttm_resource_fini(man, res);
KUNIT_ASSERT_TRUE(test, list_empty(&res->lru));
KUNIT_ASSERT_EQ(test, man->usage, 0);
}
static void ttm_resource_manager_init_basic(struct kunit *test)
{
struct ttm_resource_test_priv *priv = test->priv;
struct ttm_resource_manager *man;
size_t size = SZ_16K;
man = kunit_kzalloc(test, sizeof(*man), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, man);
ttm_resource_manager_init(man, priv->devs->ttm_dev, size);
KUNIT_ASSERT_PTR_EQ(test, man->bdev, priv->devs->ttm_dev);
KUNIT_ASSERT_EQ(test, man->size, size);
KUNIT_ASSERT_EQ(test, man->usage, 0);
KUNIT_ASSERT_NULL(test, man->move);
KUNIT_ASSERT_NOT_NULL(test, &man->move_lock);
for (int i = 0; i < TTM_MAX_BO_PRIORITY; ++i)
KUNIT_ASSERT_TRUE(test, list_empty(&man->lru[i]));
}
static void ttm_resource_manager_usage_basic(struct kunit *test)
{
struct ttm_resource_test_priv *priv = test->priv;
struct ttm_resource *res;
struct ttm_buffer_object *bo;
struct ttm_place *place;
struct ttm_resource_manager *man;
uint64_t actual_usage;
ttm_init_test_mocks(test, priv, TTM_PL_SYSTEM, TTM_PL_FLAG_TOPDOWN);
bo = priv->bo;
place = priv->place;
res = kunit_kzalloc(test, sizeof(*res), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, res);
man = ttm_manager_type(priv->devs->ttm_dev, place->mem_type);
ttm_resource_init(bo, place, res);
actual_usage = ttm_resource_manager_usage(man);
KUNIT_ASSERT_EQ(test, actual_usage, RES_SIZE);
ttm_resource_fini(man, res);
}
static void ttm_resource_manager_set_used_basic(struct kunit *test)
{
struct ttm_resource_test_priv *priv = test->priv;
struct ttm_resource_manager *man;
man = ttm_manager_type(priv->devs->ttm_dev, TTM_PL_SYSTEM);
KUNIT_ASSERT_TRUE(test, man->use_type);
ttm_resource_manager_set_used(man, false);
KUNIT_ASSERT_FALSE(test, man->use_type);
}
static void ttm_sys_man_alloc_basic(struct kunit *test)
{
struct ttm_resource_test_priv *priv = test->priv;
struct ttm_resource_manager *man;
struct ttm_buffer_object *bo;
struct ttm_place *place;
struct ttm_resource *res;
uint32_t mem_type = TTM_PL_SYSTEM;
int ret;
ttm_init_test_mocks(test, priv, mem_type, 0);
bo = priv->bo;
place = priv->place;
man = ttm_manager_type(priv->devs->ttm_dev, mem_type);
ret = man->func->alloc(man, bo, place, &res);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_ASSERT_EQ(test, res->size, RES_SIZE);
KUNIT_ASSERT_EQ(test, res->mem_type, mem_type);
KUNIT_ASSERT_PTR_EQ(test, res->bo, bo);
ttm_resource_fini(man, res);
}
static void ttm_sys_man_free_basic(struct kunit *test)
{
struct ttm_resource_test_priv *priv = test->priv;
struct ttm_resource_manager *man;
struct ttm_buffer_object *bo;
struct ttm_place *place;
struct ttm_resource *res;
uint32_t mem_type = TTM_PL_SYSTEM;
ttm_init_test_mocks(test, priv, mem_type, 0);
bo = priv->bo;
place = priv->place;
res = kunit_kzalloc(test, sizeof(*res), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, res);
ttm_resource_alloc(bo, place, &res);
man = ttm_manager_type(priv->devs->ttm_dev, mem_type);
man->func->free(man, res);
KUNIT_ASSERT_TRUE(test, list_empty(&man->lru[bo->priority]));
KUNIT_ASSERT_EQ(test, man->usage, 0);
}
static struct kunit_case ttm_resource_test_cases[] = {
KUNIT_CASE_PARAM(ttm_resource_init_basic, ttm_resource_gen_params),
KUNIT_CASE(ttm_resource_init_pinned),
KUNIT_CASE(ttm_resource_fini_basic),
KUNIT_CASE(ttm_resource_manager_init_basic),
KUNIT_CASE(ttm_resource_manager_usage_basic),
KUNIT_CASE(ttm_resource_manager_set_used_basic),
KUNIT_CASE(ttm_sys_man_alloc_basic),
KUNIT_CASE(ttm_sys_man_free_basic),
{}
};
static struct kunit_suite ttm_resource_test_suite = {
.name = "ttm_resource",
.init = ttm_resource_test_init,
.exit = ttm_resource_test_fini,
.test_cases = ttm_resource_test_cases,
};
kunit_test_suites(&ttm_resource_test_suite);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,295 @@
// SPDX-License-Identifier: GPL-2.0 AND MIT
/*
* Copyright © 2023 Intel Corporation
*/
#include <linux/shmem_fs.h>
#include <drm/ttm/ttm_tt.h>
#include "ttm_kunit_helpers.h"
#define BO_SIZE SZ_4K
struct ttm_tt_test_case {
const char *description;
uint32_t size;
uint32_t extra_pages_num;
};
static int ttm_tt_test_init(struct kunit *test)
{
struct ttm_test_devices *priv;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, priv);
priv = ttm_test_devices_all(test);
test->priv = priv;
return 0;
}
static const struct ttm_tt_test_case ttm_tt_init_basic_cases[] = {
{
.description = "Page-aligned size",
.size = SZ_4K,
},
{
.description = "Extra pages requested",
.size = SZ_4K,
.extra_pages_num = 1,
},
};
static void ttm_tt_init_case_desc(const struct ttm_tt_test_case *t,
char *desc)
{
strscpy(desc, t->description, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(ttm_tt_init_basic, ttm_tt_init_basic_cases,
ttm_tt_init_case_desc);
static void ttm_tt_init_basic(struct kunit *test)
{
const struct ttm_tt_test_case *params = test->param_value;
struct ttm_buffer_object *bo;
struct ttm_tt *tt;
uint32_t page_flags = TTM_TT_FLAG_ZERO_ALLOC;
enum ttm_caching caching = ttm_cached;
uint32_t extra_pages = params->extra_pages_num;
int num_pages = params->size >> PAGE_SHIFT;
int err;
tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tt);
bo = ttm_bo_kunit_init(test, test->priv, params->size);
err = ttm_tt_init(tt, bo, page_flags, caching, extra_pages);
KUNIT_ASSERT_EQ(test, err, 0);
KUNIT_ASSERT_EQ(test, tt->num_pages, num_pages + extra_pages);
KUNIT_ASSERT_EQ(test, tt->page_flags, page_flags);
KUNIT_ASSERT_EQ(test, tt->caching, caching);
KUNIT_ASSERT_NULL(test, tt->dma_address);
KUNIT_ASSERT_NULL(test, tt->swap_storage);
}
static void ttm_tt_init_misaligned(struct kunit *test)
{
struct ttm_buffer_object *bo;
struct ttm_tt *tt;
enum ttm_caching caching = ttm_cached;
uint32_t size = SZ_8K;
int num_pages = (size + SZ_4K) >> PAGE_SHIFT;
int err;
tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tt);
bo = ttm_bo_kunit_init(test, test->priv, size);
/* Make the object size misaligned */
bo->base.size += 1;
err = ttm_tt_init(tt, bo, 0, caching, 0);
KUNIT_ASSERT_EQ(test, err, 0);
KUNIT_ASSERT_EQ(test, tt->num_pages, num_pages);
}
static void ttm_tt_fini_basic(struct kunit *test)
{
struct ttm_buffer_object *bo;
struct ttm_tt *tt;
enum ttm_caching caching = ttm_cached;
int err;
tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tt);
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
err = ttm_tt_init(tt, bo, 0, caching, 0);
KUNIT_ASSERT_EQ(test, err, 0);
KUNIT_ASSERT_NOT_NULL(test, tt->pages);
ttm_tt_fini(tt);
KUNIT_ASSERT_NULL(test, tt->pages);
}
static void ttm_tt_fini_sg(struct kunit *test)
{
struct ttm_buffer_object *bo;
struct ttm_tt *tt;
enum ttm_caching caching = ttm_cached;
int err;
tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tt);
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
err = ttm_sg_tt_init(tt, bo, 0, caching);
KUNIT_ASSERT_EQ(test, err, 0);
KUNIT_ASSERT_NOT_NULL(test, tt->dma_address);
ttm_tt_fini(tt);
KUNIT_ASSERT_NULL(test, tt->dma_address);
}
static void ttm_tt_fini_shmem(struct kunit *test)
{
struct ttm_buffer_object *bo;
struct ttm_tt *tt;
struct file *shmem;
enum ttm_caching caching = ttm_cached;
int err;
tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tt);
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
err = ttm_tt_init(tt, bo, 0, caching, 0);
KUNIT_ASSERT_EQ(test, err, 0);
shmem = shmem_file_setup("ttm swap", BO_SIZE, 0);
tt->swap_storage = shmem;
ttm_tt_fini(tt);
KUNIT_ASSERT_NULL(test, tt->swap_storage);
}
static void ttm_tt_create_basic(struct kunit *test)
{
struct ttm_buffer_object *bo;
int err;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
bo->type = ttm_bo_type_device;
dma_resv_lock(bo->base.resv, NULL);
err = ttm_tt_create(bo, false);
dma_resv_unlock(bo->base.resv);
KUNIT_EXPECT_EQ(test, err, 0);
KUNIT_EXPECT_NOT_NULL(test, bo->ttm);
/* Free manually, as it was allocated outside of KUnit */
kfree(bo->ttm);
}
static void ttm_tt_create_invalid_bo_type(struct kunit *test)
{
struct ttm_buffer_object *bo;
int err;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
bo->type = ttm_bo_type_sg + 1;
dma_resv_lock(bo->base.resv, NULL);
err = ttm_tt_create(bo, false);
dma_resv_unlock(bo->base.resv);
KUNIT_EXPECT_EQ(test, err, -EINVAL);
KUNIT_EXPECT_NULL(test, bo->ttm);
}
static void ttm_tt_create_ttm_exists(struct kunit *test)
{
struct ttm_buffer_object *bo;
struct ttm_tt *tt;
enum ttm_caching caching = ttm_cached;
int err;
tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tt);
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
err = ttm_tt_init(tt, bo, 0, caching, 0);
KUNIT_ASSERT_EQ(test, err, 0);
bo->ttm = tt;
dma_resv_lock(bo->base.resv, NULL);
err = ttm_tt_create(bo, false);
dma_resv_unlock(bo->base.resv);
/* Expect to keep the previous TTM */
KUNIT_ASSERT_EQ(test, err, 0);
KUNIT_ASSERT_PTR_EQ(test, tt, bo->ttm);
}
static struct ttm_tt *ttm_tt_null_create(struct ttm_buffer_object *bo,
uint32_t page_flags)
{
return NULL;
}
static struct ttm_device_funcs ttm_dev_empty_funcs = {
.ttm_tt_create = ttm_tt_null_create,
};
static void ttm_tt_create_failed(struct kunit *test)
{
const struct ttm_test_devices *devs = test->priv;
struct ttm_buffer_object *bo;
int err;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
/* Update ttm_device_funcs so we don't alloc ttm_tt */
devs->ttm_dev->funcs = &ttm_dev_empty_funcs;
dma_resv_lock(bo->base.resv, NULL);
err = ttm_tt_create(bo, false);
dma_resv_unlock(bo->base.resv);
KUNIT_ASSERT_EQ(test, err, -ENOMEM);
}
static void ttm_tt_destroy_basic(struct kunit *test)
{
const struct ttm_test_devices *devs = test->priv;
struct ttm_buffer_object *bo;
int err;
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
dma_resv_lock(bo->base.resv, NULL);
err = ttm_tt_create(bo, false);
dma_resv_unlock(bo->base.resv);
KUNIT_ASSERT_EQ(test, err, 0);
KUNIT_ASSERT_NOT_NULL(test, bo->ttm);
ttm_tt_destroy(devs->ttm_dev, bo->ttm);
}
static struct kunit_case ttm_tt_test_cases[] = {
KUNIT_CASE_PARAM(ttm_tt_init_basic, ttm_tt_init_basic_gen_params),
KUNIT_CASE(ttm_tt_init_misaligned),
KUNIT_CASE(ttm_tt_fini_basic),
KUNIT_CASE(ttm_tt_fini_sg),
KUNIT_CASE(ttm_tt_fini_shmem),
KUNIT_CASE(ttm_tt_create_basic),
KUNIT_CASE(ttm_tt_create_invalid_bo_type),
KUNIT_CASE(ttm_tt_create_ttm_exists),
KUNIT_CASE(ttm_tt_create_failed),
KUNIT_CASE(ttm_tt_destroy_basic),
{}
};
static struct kunit_suite ttm_tt_test_suite = {
.name = "ttm_tt",
.init = ttm_tt_test_init,
.exit = ttm_test_devices_fini,
.test_cases = ttm_tt_test_cases,
};
kunit_test_suites(&ttm_tt_test_suite);
MODULE_LICENSE("GPL");

View File

@ -30,6 +30,8 @@
#include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_resource.h>
#include <drm/drm_util.h>
/**
* ttm_lru_bulk_move_init - initialize a bulk move structure
* @bulk: the structure to init
@ -240,6 +242,7 @@ int ttm_resource_alloc(struct ttm_buffer_object *bo,
spin_unlock(&bo->bdev->lru_lock);
return 0;
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_resource_alloc);
void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res)
{

View File

@ -36,6 +36,7 @@
#include <linux/file.h>
#include <linux/module.h>
#include <drm/drm_cache.h>
#include <drm/drm_util.h>
#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_tt.h>
@ -91,6 +92,7 @@ int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc)
return 0;
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_tt_create);
/*
* Allocates storage for pointers to the pages that back the ttm.
@ -129,6 +131,7 @@ void ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *ttm)
{
bdev->funcs->ttm_tt_destroy(bdev, ttm);
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_tt_destroy);
static void ttm_tt_init_fields(struct ttm_tt *ttm,
struct ttm_buffer_object *bo,

View File

@ -260,11 +260,26 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
return 0;
}
static int v3d_debugfs_mm(struct seq_file *m, void *unused)
{
struct drm_printer p = drm_seq_file_printer(m);
struct drm_debugfs_entry *entry = m->private;
struct drm_device *dev = entry->dev;
struct v3d_dev *v3d = to_v3d_dev(dev);
spin_lock(&v3d->mm_lock);
drm_mm_print(&v3d->mm, &p);
spin_unlock(&v3d->mm_lock);
return 0;
}
static const struct drm_debugfs_info v3d_debugfs_list[] = {
{"v3d_ident", v3d_v3d_debugfs_ident, 0},
{"v3d_regs", v3d_v3d_debugfs_regs, 0},
{"measure_clock", v3d_measure_clock, 0},
{"bo_stats", v3d_debugfs_bo_stats, 0},
{"v3d_mm", v3d_debugfs_mm, 0},
};
void

View File

@ -1497,16 +1497,16 @@ static int vc4_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct vc4_bo *bo;
int ret;
if (!state->fb)
return 0;
bo = to_vc4_bo(&drm_fb_dma_get_gem_obj(state->fb, 0)->base);
drm_gem_plane_helper_prepare_fb(plane, state);
if (plane->state->fb == state->fb)
return 0;
ret = drm_gem_plane_helper_prepare_fb(plane, state);
if (ret)
return ret;
return vc4_bo_inc_usecnt(bo);
}
@ -1516,7 +1516,7 @@ static void vc4_cleanup_fb(struct drm_plane *plane,
{
struct vc4_bo *bo;
if (plane->state->fb == state->fb || !state->fb)
if (!state->fb)
return;
bo = to_vc4_bo(&drm_fb_dma_get_gem_obj(state->fb, 0)->base);

View File

@ -99,8 +99,8 @@ virtio_gpu_parse_deps(struct virtio_gpu_submit *submit)
return 0;
/*
* kvalloc at first tries to allocate memory using kmalloc and
* falls back to vmalloc only on failure. It also uses __GFP_NOWARN
* kvmalloc() at first tries to allocate memory using kmalloc() and
* falls back to vmalloc() only on failure. It also uses __GFP_NOWARN
* internally for allocations larger than a page size, preventing
* storm of KMSG warnings.
*/
@ -529,7 +529,7 @@ int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
virtio_gpu_submit(&submit);
/*
* Set up usr-out data after submitting the job to optimize
* Set up user-out data after submitting the job to optimize
* the job submission path.
*/
virtio_gpu_install_out_fence_fd(&submit);

View File

@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_VKMS
tristate "Virtual KMS (EXPERIMENTAL)"
depends on DRM && MMU
select DRM_KMS_HELPER
select DRM_GEM_SHMEM_HELPER
select CRC32
default n
help
Virtual Kernel Mode-Setting (VKMS) is used for testing or for
running GPU in a headless machines. Choose this option to get
a VKMS.
If M is selected the module will be called vkms.

View File

@ -123,6 +123,8 @@ static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 chan
enum lut_channel channel)
{
s64 lut_index = get_lut_index(lut, channel_value);
u16 *floor_lut_value, *ceil_lut_value;
u16 floor_channel_value, ceil_channel_value;
/*
* This checks if `struct drm_color_lut` has any gap added by the compiler
@ -130,11 +132,15 @@ static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 chan
*/
static_assert(sizeof(struct drm_color_lut) == sizeof(__u16) * 4);
u16 *floor_lut_value = (__u16 *)&lut->base[drm_fixp2int(lut_index)];
u16 *ceil_lut_value = (__u16 *)&lut->base[drm_fixp2int_ceil(lut_index)];
floor_lut_value = (__u16 *)&lut->base[drm_fixp2int(lut_index)];
if (drm_fixp2int(lut_index) == (lut->lut_length - 1))
/* We're at the end of the LUT array, use same value for ceil and floor */
ceil_lut_value = floor_lut_value;
else
ceil_lut_value = (__u16 *)&lut->base[drm_fixp2int_ceil(lut_index)];
u16 floor_channel_value = floor_lut_value[channel];
u16 ceil_channel_value = ceil_lut_value[channel];
floor_channel_value = floor_lut_value[channel];
ceil_channel_value = ceil_lut_value[channel];
return lerp_u16(floor_channel_value, ceil_channel_value,
lut_index & DRM_FIXED_DECIMAL_MASK);

View File

@ -621,10 +621,10 @@ static int vmw_resources_reserve(struct vmw_sw_context *sw_context)
* @sw_context: Pointer to the software context.
* @res_type: Resource type.
* @dirty: Whether to change dirty status.
* @converter: User-space visisble type specific information.
* @converter: User-space visible type specific information.
* @id_loc: Pointer to the location in the command buffer currently being parsed
* from where the user-space resource id handle is located.
* @p_res: Pointer to pointer to resource validalidation node. Populated on
* @p_res: Pointer to pointer to resource validation node. Populated on
* exit.
*/
static int

View File

@ -64,8 +64,11 @@ static int vmw_gmrid_man_get_node(struct ttm_resource_manager *man,
ttm_resource_init(bo, place, *res);
id = ida_alloc_max(&gman->gmr_ida, gman->max_gmr_ids - 1, GFP_KERNEL);
if (id < 0)
if (id < 0) {
ttm_resource_fini(man, *res);
kfree(*res);
return id;
}
spin_lock(&gman->lock);

View File

@ -694,6 +694,10 @@ vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane,
int ret = 0;
if (vps->surf) {
if (vps->surf_mapped) {
vmw_bo_unmap(vps->surf->res.guest_memory_bo);
vps->surf_mapped = false;
}
vmw_surface_unreference(&vps->surf);
vps->surf = NULL;
}

View File

@ -53,7 +53,6 @@ enum stdu_content_type {
* struct vmw_stdu_dirty - closure structure for the update functions
*
* @base: The base type we derive from. Used by vmw_kms_helper_dirty().
* @transfer: Transfer direction for DMA command.
* @left: Left side of bounding box.
* @right: Right side of bounding box.
* @top: Top side of bounding box.
@ -100,7 +99,7 @@ struct vmw_stdu_update_gb_image {
};
/**
* struct vmw_screen_target_display_unit
* struct vmw_screen_target_display_unit - conglomerated STDU structure
*
* @base: VMW specific DU structure
* @display_srf: surface to be displayed. The dimension of this will always
@ -208,6 +207,8 @@ static int vmw_stdu_define_st(struct vmw_private *dev_priv,
* @res: Buffer to bind to the screen target. Set to NULL to blank screen.
*
* Binding a surface to a Screen Target the same as flipping
*
* Returns: %0 on success or -errno code on failure
*/
static int vmw_stdu_bind_st(struct vmw_private *dev_priv,
struct vmw_screen_target_display_unit *stdu,
@ -314,6 +315,9 @@ static int vmw_stdu_update_st(struct vmw_private *dev_priv,
*
* @dev_priv: VMW DRM device
* @stdu: display unit to destroy
*
* Returns: %0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted.
*/
static int vmw_stdu_destroy_st(struct vmw_private *dev_priv,
struct vmw_screen_target_display_unit *stdu)
@ -536,7 +540,8 @@ static void vmw_stdu_bo_cpu_commit(struct vmw_kms_dirty *dirty)
* If DMA-ing till the screen target system, the function will also notify
* the screen target system that a bounding box of the cliprects has been
* updated.
* Returns 0 on success, negative error code on failure. -ERESTARTSYS if
*
* Returns: %0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted.
*/
int vmw_kms_stdu_readback(struct vmw_private *dev_priv,
@ -703,7 +708,7 @@ static void vmw_kms_stdu_surface_fifo_commit(struct vmw_kms_dirty *dirty)
* case the device has already synchronized.
* @crtc: If crtc is passed, perform surface dirty on that crtc only.
*
* Returns 0 on success, negative error code on failure. -ERESTARTSYS if
* Returns: %0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted.
*/
int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv,
@ -887,7 +892,7 @@ vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane,
* backed by a buffer object. The display surface is pinned here, and it'll
* be unpinned in .cleanup_fb()
*
* Returns 0 on success
* Returns: %0 on success
*/
static int
vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane,
@ -1465,6 +1470,8 @@ static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = {
* This function is called once per CRTC, and allocates one Screen Target
* display unit to represent that CRTC. Since the SVGA device does not separate
* out encoder and connector, they are represented as part of the STDU as well.
*
* Returns: %0 on success or -errno code on failure
*/
static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
{

View File

@ -44,7 +44,6 @@
* struct vmw_user_surface - User-space visible surface resource
*
* @prime: The TTM prime object.
* @base: The TTM base object handling user-space visibility.
* @srf: The surface metadata.
* @master: Master of the creating client. Used for security check.
*/

View File

@ -108,7 +108,7 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
*/
#if defined CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER && \
defined CONFIG_ACPI_BGRT
static void efifb_copy_bmp(u8 *src, u32 *dst, int width, struct screen_info *si)
static void efifb_copy_bmp(u8 *src, u32 *dst, int width, const struct screen_info *si)
{
u8 r, g, b;
@ -130,7 +130,7 @@ static void efifb_copy_bmp(u8 *src, u32 *dst, int width, struct screen_info *si)
* resolution still fits, it will be displayed very close to the right edge of
* the display looking quite bad. This function checks for this.
*/
static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width)
static bool efifb_bgrt_sanity_check(const struct screen_info *si, u32 bmp_width)
{
/*
* All x86 firmwares horizontally center the image (the yoffset
@ -141,16 +141,15 @@ static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width)
return bgrt_tab.image_offset_x == expected_xoffset;
}
#else
static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width)
static bool efifb_bgrt_sanity_check(const struct screen_info *si, u32 bmp_width)
{
return true;
}
#endif
static void efifb_show_boot_graphics(struct fb_info *info)
static void efifb_show_boot_graphics(struct fb_info *info, const struct screen_info *si)
{
u32 bmp_width, bmp_height, bmp_pitch, dst_x, y, src_y;
struct screen_info *si = &screen_info;
struct bmp_file_header *file_header;
struct bmp_dib_header *dib_header;
void *bgrt_image = NULL;
@ -247,7 +246,8 @@ error:
pr_warn("efifb: Ignoring BGRT: unexpected or invalid BMP data\n");
}
#else
static inline void efifb_show_boot_graphics(struct fb_info *info) {}
static inline void efifb_show_boot_graphics(struct fb_info *info, const struct screen_info *si)
{ }
#endif
/*
@ -282,7 +282,7 @@ static const struct fb_ops efifb_ops = {
.fb_setcolreg = efifb_setcolreg,
};
static int efifb_setup(char *options)
static int efifb_setup(struct screen_info *si, char *options)
{
char *this_opt;
@ -290,16 +290,16 @@ static int efifb_setup(char *options)
while ((this_opt = strsep(&options, ",")) != NULL) {
if (!*this_opt) continue;
efifb_setup_from_dmi(&screen_info, this_opt);
efifb_setup_from_dmi(si, this_opt);
if (!strncmp(this_opt, "base:", 5))
screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0);
si->lfb_base = simple_strtoul(this_opt+5, NULL, 0);
else if (!strncmp(this_opt, "stride:", 7))
screen_info.lfb_linelength = simple_strtoul(this_opt+7, NULL, 0) * 4;
si->lfb_linelength = simple_strtoul(this_opt+7, NULL, 0) * 4;
else if (!strncmp(this_opt, "height:", 7))
screen_info.lfb_height = simple_strtoul(this_opt+7, NULL, 0);
si->lfb_height = simple_strtoul(this_opt+7, NULL, 0);
else if (!strncmp(this_opt, "width:", 6))
screen_info.lfb_width = simple_strtoul(this_opt+6, NULL, 0);
si->lfb_width = simple_strtoul(this_opt+6, NULL, 0);
else if (!strcmp(this_opt, "nowc"))
mem_flags &= ~EFI_MEMORY_WC;
else if (!strcmp(this_opt, "nobgrt"))
@ -310,15 +310,15 @@ static int efifb_setup(char *options)
return 0;
}
static inline bool fb_base_is_valid(void)
static inline bool fb_base_is_valid(struct screen_info *si)
{
if (screen_info.lfb_base)
if (si->lfb_base)
return true;
if (!(screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE))
if (!(si->capabilities & VIDEO_CAPABILITY_64BIT_BASE))
return false;
if (screen_info.ext_lfb_base)
if (si->ext_lfb_base)
return true;
return false;
@ -329,7 +329,10 @@ static ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return sprintf(buf, fmt "\n", (screen_info.lfb_##name)); \
struct screen_info *si = dev_get_platdata(dev); \
if (!si) \
return -ENODEV; \
return sprintf(buf, fmt "\n", (si->lfb_##name)); \
} \
static DEVICE_ATTR_RO(name)
@ -356,6 +359,7 @@ static u64 bar_offset;
static int efifb_probe(struct platform_device *dev)
{
struct screen_info *si;
struct fb_info *info;
struct efifb_par *par;
int err, orientation;
@ -365,48 +369,60 @@ static int efifb_probe(struct platform_device *dev)
char *option = NULL;
efi_memory_desc_t md;
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
/*
* If we fail probing the device, the kernel might try a different
* driver. We get a copy of the attached screen_info, so that we can
* modify its values without affecting later drivers.
*/
si = dev_get_platdata(&dev->dev);
if (!si)
return -ENODEV;
si = devm_kmemdup(&dev->dev, si, sizeof(*si), GFP_KERNEL);
if (!si)
return -ENOMEM;
if (si->orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
return -ENODEV;
if (fb_get_options("efifb", &option))
return -ENODEV;
efifb_setup(option);
efifb_setup(si, option);
/* We don't get linelength from UGA Draw Protocol, only from
* EFI Graphics Protocol. So if it's not in DMI, and it's not
* passed in from the user, we really can't use the framebuffer.
*/
if (!screen_info.lfb_linelength)
if (!si->lfb_linelength)
return -ENODEV;
if (!screen_info.lfb_depth)
screen_info.lfb_depth = 32;
if (!screen_info.pages)
screen_info.pages = 1;
if (!fb_base_is_valid()) {
if (!si->lfb_depth)
si->lfb_depth = 32;
if (!si->pages)
si->pages = 1;
if (!fb_base_is_valid(si)) {
printk(KERN_DEBUG "efifb: invalid framebuffer address\n");
return -ENODEV;
}
printk(KERN_INFO "efifb: probing for efifb\n");
/* just assume they're all unset if any are */
if (!screen_info.blue_size) {
screen_info.blue_size = 8;
screen_info.blue_pos = 0;
screen_info.green_size = 8;
screen_info.green_pos = 8;
screen_info.red_size = 8;
screen_info.red_pos = 16;
screen_info.rsvd_size = 8;
screen_info.rsvd_pos = 24;
if (!si->blue_size) {
si->blue_size = 8;
si->blue_pos = 0;
si->green_size = 8;
si->green_pos = 8;
si->red_size = 8;
si->red_pos = 16;
si->rsvd_size = 8;
si->rsvd_pos = 24;
}
efifb_fix.smem_start = screen_info.lfb_base;
efifb_fix.smem_start = si->lfb_base;
if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
if (si->capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
u64 ext_lfb_base;
ext_lfb_base = (u64)(unsigned long)screen_info.ext_lfb_base << 32;
ext_lfb_base = (u64)(unsigned long)si->ext_lfb_base << 32;
efifb_fix.smem_start |= ext_lfb_base;
}
@ -417,10 +433,10 @@ static int efifb_probe(struct platform_device *dev)
efifb_fix.smem_start = bar_resource->start + bar_offset;
}
efifb_defined.bits_per_pixel = screen_info.lfb_depth;
efifb_defined.xres = screen_info.lfb_width;
efifb_defined.yres = screen_info.lfb_height;
efifb_fix.line_length = screen_info.lfb_linelength;
efifb_defined.bits_per_pixel = si->lfb_depth;
efifb_defined.xres = si->lfb_width;
efifb_defined.yres = si->lfb_height;
efifb_fix.line_length = si->lfb_linelength;
/* size_vmode -- that is the amount of memory needed for the
* used video mode, i.e. the minimum amount of
@ -430,7 +446,7 @@ static int efifb_probe(struct platform_device *dev)
/* size_total -- all video memory we have. Used for
* entries, ressource allocation and bounds
* checking. */
size_total = screen_info.lfb_size;
size_total = si->lfb_size;
if (size_total < size_vmode)
size_total = size_vmode;
@ -505,14 +521,14 @@ static int efifb_probe(struct platform_device *dev)
goto err_release_fb;
}
efifb_show_boot_graphics(info);
efifb_show_boot_graphics(info, si);
pr_info("efifb: framebuffer at 0x%lx, using %dk, total %dk\n",
efifb_fix.smem_start, size_remap/1024, size_total/1024);
pr_info("efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
efifb_defined.xres, efifb_defined.yres,
efifb_defined.bits_per_pixel, efifb_fix.line_length,
screen_info.pages);
si->pages);
efifb_defined.xres_virtual = efifb_defined.xres;
efifb_defined.yres_virtual = efifb_fix.smem_len /
@ -526,26 +542,26 @@ static int efifb_probe(struct platform_device *dev)
efifb_defined.left_margin = (efifb_defined.xres / 8) & 0xf8;
efifb_defined.hsync_len = (efifb_defined.xres / 8) & 0xf8;
efifb_defined.red.offset = screen_info.red_pos;
efifb_defined.red.length = screen_info.red_size;
efifb_defined.green.offset = screen_info.green_pos;
efifb_defined.green.length = screen_info.green_size;
efifb_defined.blue.offset = screen_info.blue_pos;
efifb_defined.blue.length = screen_info.blue_size;
efifb_defined.transp.offset = screen_info.rsvd_pos;
efifb_defined.transp.length = screen_info.rsvd_size;
efifb_defined.red.offset = si->red_pos;
efifb_defined.red.length = si->red_size;
efifb_defined.green.offset = si->green_pos;
efifb_defined.green.length = si->green_size;
efifb_defined.blue.offset = si->blue_pos;
efifb_defined.blue.length = si->blue_size;
efifb_defined.transp.offset = si->rsvd_pos;
efifb_defined.transp.length = si->rsvd_size;
pr_info("efifb: %s: "
"size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
"Truecolor",
screen_info.rsvd_size,
screen_info.red_size,
screen_info.green_size,
screen_info.blue_size,
screen_info.rsvd_pos,
screen_info.red_pos,
screen_info.green_pos,
screen_info.blue_pos);
si->rsvd_size,
si->red_size,
si->green_size,
si->blue_size,
si->rsvd_pos,
si->red_pos,
si->green_pos,
si->blue_pos);
efifb_fix.ypanstep = 0;
efifb_fix.ywrapstep = 0;

View File

@ -470,7 +470,7 @@ static int simplefb_attach_genpds(struct simplefb_par *par,
if (err == -ENOENT)
return 0;
dev_info(dev, "failed to parse power-domains: %d\n", err);
dev_err(dev, "failed to parse power-domains: %d\n", err);
return err;
}

View File

@ -243,6 +243,7 @@ static int vesafb_setup(char *options)
static int vesafb_probe(struct platform_device *dev)
{
struct screen_info *si;
struct fb_info *info;
struct vesafb_par *par;
int i, err;
@ -251,21 +252,33 @@ static int vesafb_probe(struct platform_device *dev)
unsigned int size_total;
char *option = NULL;
/*
* If we fail probing the device, the kernel might try a different
* driver. We get a copy of the attached screen_info, so that we can
* modify its values without affecting later drivers.
*/
si = dev_get_platdata(&dev->dev);
if (!si)
return -ENODEV;
si = devm_kmemdup(&dev->dev, si, sizeof(*si), GFP_KERNEL);
if (!si)
return -ENOMEM;
/* ignore error return of fb_get_options */
fb_get_options("vesafb", &option);
vesafb_setup(option);
if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB)
if (si->orig_video_isVGA != VIDEO_TYPE_VLFB)
return -ENODEV;
vga_compat = (screen_info.capabilities & 2) ? 0 : 1;
vesafb_fix.smem_start = screen_info.lfb_base;
vesafb_defined.bits_per_pixel = screen_info.lfb_depth;
vga_compat = (si->capabilities & 2) ? 0 : 1;
vesafb_fix.smem_start = si->lfb_base;
vesafb_defined.bits_per_pixel = si->lfb_depth;
if (15 == vesafb_defined.bits_per_pixel)
vesafb_defined.bits_per_pixel = 16;
vesafb_defined.xres = screen_info.lfb_width;
vesafb_defined.yres = screen_info.lfb_height;
vesafb_fix.line_length = screen_info.lfb_linelength;
vesafb_defined.xres = si->lfb_width;
vesafb_defined.yres = si->lfb_height;
vesafb_fix.line_length = si->lfb_linelength;
vesafb_fix.visual = (vesafb_defined.bits_per_pixel == 8) ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
@ -277,7 +290,7 @@ static int vesafb_probe(struct platform_device *dev)
/* size_total -- all video memory we have. Used for mtrr
* entries, resource allocation and bounds
* checking. */
size_total = screen_info.lfb_size * 65536;
size_total = si->lfb_size * 65536;
if (vram_total)
size_total = vram_total * 1024 * 1024;
if (size_total < size_vmode)
@ -297,7 +310,7 @@ static int vesafb_probe(struct platform_device *dev)
vesafb_fix.smem_len = size_remap;
#ifndef __i386__
screen_info.vesapm_seg = 0;
si->vesapm_seg = 0;
#endif
if (!request_mem_region(vesafb_fix.smem_start, size_total, "vesafb")) {
@ -317,23 +330,26 @@ static int vesafb_probe(struct platform_device *dev)
par = info->par;
info->pseudo_palette = par->pseudo_palette;
par->base = screen_info.lfb_base;
par->base = si->lfb_base;
par->size = size_total;
printk(KERN_INFO "vesafb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
vesafb_defined.xres, vesafb_defined.yres, vesafb_defined.bits_per_pixel, vesafb_fix.line_length, screen_info.pages);
vesafb_defined.xres, vesafb_defined.yres, vesafb_defined.bits_per_pixel,
vesafb_fix.line_length, si->pages);
if (screen_info.vesapm_seg) {
if (si->vesapm_seg) {
printk(KERN_INFO "vesafb: protected mode interface info at %04x:%04x\n",
screen_info.vesapm_seg,screen_info.vesapm_off);
si->vesapm_seg, si->vesapm_off);
}
if (screen_info.vesapm_seg < 0xc000)
if (si->vesapm_seg < 0xc000)
ypan = pmi_setpal = 0; /* not available or some DOS TSR ... */
if (ypan || pmi_setpal) {
unsigned long pmi_phys;
unsigned short *pmi_base;
pmi_base = (unsigned short*)phys_to_virt(((unsigned long)screen_info.vesapm_seg << 4) + screen_info.vesapm_off);
pmi_phys = ((unsigned long)si->vesapm_seg << 4) + si->vesapm_off;
pmi_base = (unsigned short *)phys_to_virt(pmi_phys);
pmi_start = (void*)((char*)pmi_base + pmi_base[1]);
pmi_pal = (void*)((char*)pmi_base + pmi_base[2]);
printk(KERN_INFO "vesafb: pmi: set display start = %p, set palette = %p\n",pmi_start,pmi_pal);
@ -377,14 +393,14 @@ static int vesafb_probe(struct platform_device *dev)
vesafb_defined.left_margin = (vesafb_defined.xres / 8) & 0xf8;
vesafb_defined.hsync_len = (vesafb_defined.xres / 8) & 0xf8;
vesafb_defined.red.offset = screen_info.red_pos;
vesafb_defined.red.length = screen_info.red_size;
vesafb_defined.green.offset = screen_info.green_pos;
vesafb_defined.green.length = screen_info.green_size;
vesafb_defined.blue.offset = screen_info.blue_pos;
vesafb_defined.blue.length = screen_info.blue_size;
vesafb_defined.transp.offset = screen_info.rsvd_pos;
vesafb_defined.transp.length = screen_info.rsvd_size;
vesafb_defined.red.offset = si->red_pos;
vesafb_defined.red.length = si->red_size;
vesafb_defined.green.offset = si->green_pos;
vesafb_defined.green.length = si->green_size;
vesafb_defined.blue.offset = si->blue_pos;
vesafb_defined.blue.length = si->blue_size;
vesafb_defined.transp.offset = si->rsvd_pos;
vesafb_defined.transp.length = si->rsvd_size;
if (vesafb_defined.bits_per_pixel <= 8) {
depth = vesafb_defined.green.length;
@ -399,14 +415,14 @@ static int vesafb_probe(struct platform_device *dev)
(vesafb_defined.bits_per_pixel > 8) ?
"Truecolor" : (vga_compat || pmi_setpal) ?
"Pseudocolor" : "Static Pseudocolor",
screen_info.rsvd_size,
screen_info.red_size,
screen_info.green_size,
screen_info.blue_size,
screen_info.rsvd_pos,
screen_info.red_pos,
screen_info.green_pos,
screen_info.blue_pos);
si->rsvd_size,
si->red_size,
si->green_size,
si->blue_size,
si->rsvd_pos,
si->red_pos,
si->green_pos,
si->blue_pos);
vesafb_fix.ypanstep = ypan ? 1 : 0;
vesafb_fix.ywrapstep = (ypan>1) ? 1 : 0;

View File

@ -346,25 +346,29 @@ struct __drm_private_objs_state {
};
/**
* struct drm_atomic_state - the global state object for atomic updates
* @ref: count of all references to this state (will not be freed until zero)
* @dev: parent DRM device
* @async_update: hint for asynchronous plane update
* @planes: pointer to array of structures with per-plane data
* @crtcs: pointer to array of CRTC pointers
* @num_connector: size of the @connectors and @connector_states arrays
* @connectors: pointer to array of structures with per-connector data
* @num_private_objs: size of the @private_objs array
* @private_objs: pointer to array of private object pointers
* @acquire_ctx: acquire context for this atomic modeset state update
* struct drm_atomic_state - Atomic commit structure
*
* This structure is the kernel counterpart of @drm_mode_atomic and represents
* an atomic commit that transitions from an old to a new display state. It
* contains all the objects affected by the atomic commit and both the new
* state structures and pointers to the old state structures for
* these.
*
* States are added to an atomic update by calling drm_atomic_get_crtc_state(),
* drm_atomic_get_plane_state(), drm_atomic_get_connector_state(), or for
* private state structures, drm_atomic_get_private_obj_state().
*/
struct drm_atomic_state {
/**
* @ref:
*
* Count of all references to this update (will not be freed until zero).
*/
struct kref ref;
/**
* @dev: Parent DRM Device.
*/
struct drm_device *dev;
/**
@ -388,7 +392,12 @@ struct drm_atomic_state {
* flag are not allowed.
*/
bool legacy_cursor_update : 1;
/**
* @async_update: hint for asynchronous plane update
*/
bool async_update : 1;
/**
* @duplicated:
*
@ -398,13 +407,52 @@ struct drm_atomic_state {
* states.
*/
bool duplicated : 1;
/**
* @planes:
*
* Pointer to array of @drm_plane and @drm_plane_state part of this
* update.
*/
struct __drm_planes_state *planes;
/**
* @crtcs:
*
* Pointer to array of @drm_crtc and @drm_crtc_state part of this
* update.
*/
struct __drm_crtcs_state *crtcs;
/**
* @num_connector: size of the @connectors array
*/
int num_connector;
/**
* @connectors:
*
* Pointer to array of @drm_connector and @drm_connector_state part of
* this update.
*/
struct __drm_connnectors_state *connectors;
/**
* @num_private_objs: size of the @private_objs array
*/
int num_private_objs;
/**
* @private_objs:
*
* Pointer to array of @drm_private_obj and @drm_private_obj_state part
* of this update.
*/
struct __drm_private_objs_state *private_objs;
/**
* @acquire_ctx: acquire context for this atomic modeset state update
*/
struct drm_modeset_acquire_ctx *acquire_ctx;
/**

View File

@ -24,11 +24,14 @@
#define __DRM_EDID_H__
#include <linux/types.h>
#include <linux/hdmi.h>
#include <drm/drm_mode.h>
enum hdmi_quantization_range;
struct drm_connector;
struct drm_device;
struct drm_display_mode;
struct drm_edid;
struct hdmi_avi_infoframe;
struct hdmi_vendor_infoframe;
struct i2c_adapter;
#define EDID_LENGTH 128
@ -46,7 +49,7 @@ struct est_timings {
u8 t1;
u8 t2;
u8 mfg_rsvd;
} __attribute__((packed));
} __packed;
/* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */
#define EDID_TIMING_ASPECT_SHIFT 6
@ -59,7 +62,7 @@ struct est_timings {
struct std_timing {
u8 hsize; /* need to multiply by 8 then add 248 */
u8 vfreq_aspect;
} __attribute__((packed));
} __packed;
#define DRM_EDID_PT_HSYNC_POSITIVE (1 << 1)
#define DRM_EDID_PT_VSYNC_POSITIVE (1 << 2)
@ -85,12 +88,12 @@ struct detailed_pixel_timing {
u8 hborder;
u8 vborder;
u8 misc;
} __attribute__((packed));
} __packed;
/* If it's not pixel timing, it'll be one of the below */
struct detailed_data_string {
u8 str[13];
} __attribute__((packed));
} __packed;
#define DRM_EDID_RANGE_OFFSET_MIN_VFREQ (1 << 0) /* 1.4 */
#define DRM_EDID_RANGE_OFFSET_MAX_VFREQ (1 << 1) /* 1.4 */
@ -120,7 +123,7 @@ struct detailed_data_monitor_range {
__le16 m;
u8 k;
u8 j; /* need to divide by 2 */
} __attribute__((packed)) gtf2;
} __packed gtf2;
struct {
u8 version;
u8 data1; /* high 6 bits: extra clock resolution */
@ -129,27 +132,27 @@ struct detailed_data_monitor_range {
u8 flags; /* preferred aspect and blanking support */
u8 supported_scalings;
u8 preferred_refresh;
} __attribute__((packed)) cvt;
} __attribute__((packed)) formula;
} __attribute__((packed));
} __packed cvt;
} __packed formula;
} __packed;
struct detailed_data_wpindex {
u8 white_yx_lo; /* Lower 2 bits each */
u8 white_x_hi;
u8 white_y_hi;
u8 gamma; /* need to divide by 100 then add 1 */
} __attribute__((packed));
} __packed;
struct detailed_data_color_point {
u8 windex1;
u8 wpindex1[3];
u8 windex2;
u8 wpindex2[3];
} __attribute__((packed));
} __packed;
struct cvt_timing {
u8 code[3];
} __attribute__((packed));
} __packed;
struct detailed_non_pixel {
u8 pad1;
@ -163,8 +166,8 @@ struct detailed_non_pixel {
struct detailed_data_wpindex color;
struct std_timing timings[6];
struct cvt_timing cvt[4];
} __attribute__((packed)) data;
} __attribute__((packed));
} __packed data;
} __packed;
#define EDID_DETAIL_EST_TIMINGS 0xf7
#define EDID_DETAIL_CVT_3BYTE 0xf8
@ -181,8 +184,8 @@ struct detailed_timing {
union {
struct detailed_pixel_timing pixel_data;
struct detailed_non_pixel other_data;
} __attribute__((packed)) data;
} __attribute__((packed));
} __packed data;
} __packed;
#define DRM_EDID_INPUT_SERRATION_VSYNC (1 << 0)
#define DRM_EDID_INPUT_SYNC_ON_GREEN (1 << 1)
@ -307,7 +310,7 @@ struct edid {
u8 extensions;
/* Checksum */
u8 checksum;
} __attribute__((packed));
} __packed;
#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8))
@ -319,11 +322,6 @@ struct cea_sad {
u8 byte2; /* meaning depends on format */
};
struct drm_encoder;
struct drm_connector;
struct drm_connector_state;
struct drm_display_mode;
int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads);
int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb);
int drm_av_sync_delay(struct drm_connector *connector,
@ -426,8 +424,6 @@ enum hdmi_quantization_range
drm_default_rgb_quant_range(const struct drm_display_mode *mode);
int drm_add_modes_noedid(struct drm_connector *connector,
int hdisplay, int vdisplay);
void drm_set_preferred_mode(struct drm_connector *connector,
int hpref, int vpref);
int drm_edid_header_is_valid(const void *edid);
bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid,

View File

@ -95,7 +95,7 @@ static inline int drm_fixp2int_round(s64 a)
static inline int drm_fixp2int_ceil(s64 a)
{
if (a > 0)
if (a >= 0)
return drm_fixp2int(a + DRM_FIXED_ALMOST_ONE);
else
return drm_fixp2int(a - DRM_FIXED_ALMOST_ONE);

View File

@ -467,6 +467,8 @@ bool drm_mode_is_420_also(const struct drm_display_info *display,
const struct drm_display_mode *mode);
bool drm_mode_is_420(const struct drm_display_info *display,
const struct drm_display_mode *mode);
void drm_set_preferred_mode(struct drm_connector *connector,
int hpref, int vpref);
struct drm_display_mode *drm_analog_tv_mode(struct drm_device *dev,
enum drm_connector_tv_mode mode,

View File

@ -32,7 +32,6 @@ enum drm_mode_status drm_crtc_helper_mode_valid_fixed(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
const struct drm_display_mode *fixed_mode);
int drm_connector_helper_get_modes_from_ddc(struct drm_connector *connector);
int drm_connector_helper_get_modes_fixed(struct drm_connector *connector,
const struct drm_display_mode *fixed_mode);
int drm_connector_helper_get_modes(struct drm_connector *connector);

View File

@ -238,34 +238,32 @@ struct drm_nouveau_vm_init {
struct drm_nouveau_vm_bind_op {
/**
* @op: the operation type
*
* Supported values:
*
* %DRM_NOUVEAU_VM_BIND_OP_MAP - Map a GEM object to the GPU's VA
* space. Optionally, the &DRM_NOUVEAU_VM_BIND_SPARSE flag can be
* passed to instruct the kernel to create sparse mappings for the
* given range.
*
* %DRM_NOUVEAU_VM_BIND_OP_UNMAP - Unmap an existing mapping in the
* GPU's VA space. If the region the mapping is located in is a
* sparse region, new sparse mappings are created where the unmapped
* (memory backed) mapping was mapped previously. To remove a sparse
* region the &DRM_NOUVEAU_VM_BIND_SPARSE must be set.
*/
__u32 op;
/**
* @DRM_NOUVEAU_VM_BIND_OP_MAP:
*
* Map a GEM object to the GPU's VA space. Optionally, the
* &DRM_NOUVEAU_VM_BIND_SPARSE flag can be passed to instruct the kernel to
* create sparse mappings for the given range.
*/
#define DRM_NOUVEAU_VM_BIND_OP_MAP 0x0
/**
* @DRM_NOUVEAU_VM_BIND_OP_UNMAP:
*
* Unmap an existing mapping in the GPU's VA space. If the region the mapping
* is located in is a sparse region, new sparse mappings are created where the
* unmapped (memory backed) mapping was mapped previously. To remove a sparse
* region the &DRM_NOUVEAU_VM_BIND_SPARSE must be set.
*/
#define DRM_NOUVEAU_VM_BIND_OP_UNMAP 0x1
/**
* @flags: the flags for a &drm_nouveau_vm_bind_op
*
* Supported values:
*
* %DRM_NOUVEAU_VM_BIND_SPARSE - Indicates that an allocated VA
* space region should be sparse.
*/
__u32 flags;
/**
* @DRM_NOUVEAU_VM_BIND_SPARSE:
*
* Indicates that an allocated VA space region should be sparse.
*/
#define DRM_NOUVEAU_VM_BIND_SPARSE (1 << 8)
/**
* @handle: the handle of the DRM GEM object to map
@ -301,17 +299,17 @@ struct drm_nouveau_vm_bind {
__u32 op_count;
/**
* @flags: the flags for a &drm_nouveau_vm_bind ioctl
*
* Supported values:
*
* %DRM_NOUVEAU_VM_BIND_RUN_ASYNC - Indicates that the given VM_BIND
* operation should be executed asynchronously by the kernel.
*
* If this flag is not supplied the kernel executes the associated
* operations synchronously and doesn't accept any &drm_nouveau_sync
* objects.
*/
__u32 flags;
/**
* @DRM_NOUVEAU_VM_BIND_RUN_ASYNC:
*
* Indicates that the given VM_BIND operation should be executed asynchronously
* by the kernel.
*
* If this flag is not supplied the kernel executes the associated operations
* synchronously and doesn't accept any &drm_nouveau_sync objects.
*/
#define DRM_NOUVEAU_VM_BIND_RUN_ASYNC 0x1
/**
* @wait_count: the number of wait &drm_nouveau_syncs

View File

@ -242,18 +242,7 @@ struct qaic_attach_slice_entry {
* @dbc_id: In. Associate the sliced BO with this DBC.
* @handle: In. GEM handle of the BO to slice.
* @dir: In. Direction of data flow. 1 = DMA_TO_DEVICE, 2 = DMA_FROM_DEVICE
* @size: In. Total length of BO being used. This should not exceed base
* size of BO (struct drm_gem_object.base)
* For BOs being allocated using DRM_IOCTL_QAIC_CREATE_BO, size of
* BO requested is PAGE_SIZE aligned then allocated hence allocated
* BO size maybe bigger. This size should not exceed the new
* PAGE_SIZE aligned BO size.
* @dev_addr: In. Device address this slice pushes to or pulls from.
* @db_addr: In. Address of the doorbell to ring.
* @db_data: In. Data to write to the doorbell.
* @db_len: In. Size of the doorbell data in bits - 32, 16, or 8. 0 is for
* inactive doorbells.
* @offset: In. Start of this slice as an offset from the start of the BO.
* @size: Deprecated. This value is ignored and size of @handle is used instead.
*/
struct qaic_attach_slice_hdr {
__u32 count;

View File

@ -309,6 +309,8 @@ struct virtio_gpu_cmd_submit {
#define VIRTIO_GPU_CAPSET_VIRGL 1
#define VIRTIO_GPU_CAPSET_VIRGL2 2
/* 3 is reserved for gfxstream */
#define VIRTIO_GPU_CAPSET_VENUS 4
/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
struct virtio_gpu_get_capset_info {