drm-misc-next for 5.10:

UAPI Changes:
 
 Cross-subsystem Changes:
 
 Core Changes:
   - ttm: various cleanups and reworks of the API
 
 Driver Changes:
   - ast: various cleanups
   - gma500: A few fixes, conversion to GPIOd API
   - hisilicon: Change of maintainer, various reworks
   - ingenic: Clock handling and formats support improvements
   - mcde: improvements to the DSI support
   - mgag200: Support G200 desktop cards
   - mxsfb: Support the i.MX7 and i.MX8M and the alpha plane
   - panfrost: support devfreq
   - ps8640: Retrieve the EDID from eDP control, misc improvements
   - tidss: Add a workaround for AM65xx YUV formats handling
   - virtio: a few cleanups, support for virtio-gpu exported resources
   - bridges: Support the chained bridges on more drivers,
     new bridges: Toshiba TC358762, Toshiba TC358775, Lontium LT9611
   - panels: Convert to dev_ based logging, read orientation from the DT,
     various fixes, new panels: Mantix MLAF057WE51-X, Chefree CH101OLHLWH-002,
     Powertip PH800480T013, KingDisplay KD116N21-30NV-A010
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQRcEzekXsqa64kGDp7j7w1vZxhRxQUCX0fXGwAKCRDj7w1vZxhR
 xTmMAQDPmfSsBLLNnDxu4++zFrQ7OKmNSHCkVr4nAQ/yg3GVPQEAuRw6qPwPWuV3
 +jEPxaQSSmHOhx/jXfolV1tJaE/FHgA=
 =WYoO
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2020-08-27' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for 5.10:

UAPI Changes:

Cross-subsystem Changes:

Core Changes:
  - ttm: various cleanups and reworks of the API

Driver Changes:
  - ast: various cleanups
  - gma500: A few fixes, conversion to GPIOd API
  - hisilicon: Change of maintainer, various reworks
  - ingenic: Clock handling and formats support improvements
  - mcde: improvements to the DSI support
  - mgag200: Support G200 desktop cards
  - mxsfb: Support the i.MX7 and i.MX8M and the alpha plane
  - panfrost: support devfreq
  - ps8640: Retrieve the EDID from eDP control, misc improvements
  - tidss: Add a workaround for AM65xx YUV formats handling
  - virtio: a few cleanups, support for virtio-gpu exported resources
  - bridges: Support the chained bridges on more drivers,
    new bridges: Toshiba TC358762, Toshiba TC358775, Lontium LT9611
  - panels: Convert to dev_ based logging, read orientation from the DT,
    various fixes, new panels: Mantix MLAF057WE51-X, Chefree CH101OLHLWH-002,
    Powertip PH800480T013, KingDisplay KD116N21-30NV-A010

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

From: Maxime Ripard <maxime@cerno.tech>
Link: https://patchwork.freedesktop.org/patch/msgid/20200827155517.do6emeacetpturli@gilmour.lan
This commit is contained in:
Dave Airlie 2020-08-28 12:37:46 +10:00
commit cbc2e82932
296 changed files with 8748 additions and 4403 deletions

View file

@ -0,0 +1,176 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/bridge/lontium,lt9611.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Lontium LT9611 2 Port MIPI to HDMI Bridge
maintainers:
- Vinod Koul <vkoul@kernel.org>
description: |
The LT9611 is a bridge device which converts DSI to HDMI
properties:
compatible:
enum:
- lontium,lt9611
reg:
maxItems: 1
"#sound-dai-cells":
const: 1
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
description: GPIO connected to active high RESET pin.
vdd-supply:
description: Regulator for 1.8V MIPI phy power.
vcc-supply:
description: Regulator for 3.3V IO power.
ports:
type: object
properties:
"#address-cells":
const: 1
"#size-cells":
const: 0
port@0:
type: object
description: |
Primary MIPI port-1 for MIPI input
properties:
reg:
const: 0
patternProperties:
"^endpoint(@[0-9])$":
type: object
additionalProperties: false
properties:
remote-endpoint:
$ref: /schemas/types.yaml#/definitions/phandle
required:
- reg
port@1:
type: object
description: |
Additional MIPI port-2 for MIPI input, used in combination
with primary MIPI port-1 to drive higher resolution displays
properties:
reg:
const: 1
patternProperties:
"^endpoint(@[0-9])$":
type: object
additionalProperties: false
properties:
remote-endpoint:
$ref: /schemas/types.yaml#/definitions/phandle
required:
- reg
port@2:
type: object
description: |
HDMI port for HDMI output
properties:
reg:
const: 2
patternProperties:
"^endpoint(@[0-9])$":
type: object
additionalProperties: false
properties:
remote-endpoint:
$ref: /schemas/types.yaml#/definitions/phandle
required:
- reg
required:
- "#address-cells"
- "#size-cells"
- port@0
- port@2
required:
- compatible
- reg
- interrupts
- vdd-supply
- vcc-supply
- ports
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c10 {
#address-cells = <1>;
#size-cells = <0>;
hdmi-bridge@3b {
compatible = "lontium,lt9611";
reg = <0x3b>;
reset-gpios = <&tlmm 128 GPIO_ACTIVE_HIGH>;
interrupts-extended = <&tlmm 84 IRQ_TYPE_EDGE_FALLING>;
vdd-supply = <&lt9611_1v8>;
vcc-supply = <&lt9611_3v3>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lt9611_a: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
port@1 {
reg = <1>;
lt9611_b: endpoint {
remote-endpoint = <&dsi1_out>;
};
};
port@2 {
reg = <2>;
lt9611_out: endpoint {
remote-endpoint = <&hdmi_con>;
};
};
};
};
};
...

View file

@ -0,0 +1,127 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/bridge/toshiba,tc358762.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Toshiba TC358762 MIPI DSI to MIPI DPI bridge
maintainers:
- Marek Vasut <marex@denx.de>
description: |
The TC358762 is bridge device which converts MIPI DSI to MIPI DPI.
properties:
compatible:
enum:
- toshiba,tc358762
reg:
maxItems: 1
description: virtual channel number of a DSI peripheral
vddc-supply:
description: Regulator for 1.2V internal core power.
ports:
type: object
properties:
"#address-cells":
const: 1
"#size-cells":
const: 0
port@0:
type: object
additionalProperties: false
description: |
Video port for MIPI DSI input
properties:
reg:
const: 0
patternProperties:
endpoint:
type: object
additionalProperties: false
properties:
remote-endpoint: true
required:
- reg
port@1:
type: object
additionalProperties: false
description: |
Video port for MIPI DPI output (panel or connector).
properties:
reg:
const: 1
patternProperties:
endpoint:
type: object
additionalProperties: false
properties:
remote-endpoint: true
required:
- reg
required:
- "#address-cells"
- "#size-cells"
- port@0
- port@1
required:
- compatible
- reg
- vddc-supply
- ports
additionalProperties: false
examples:
- |
i2c1 {
#address-cells = <1>;
#size-cells = <0>;
bridge@0 {
reg = <0>;
compatible = "toshiba,tc358762";
vddc-supply = <&vcc_1v2_reg>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
bridge_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
port@1 {
reg = <1>;
bridge_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
};
};
};
...

View file

@ -0,0 +1,215 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/bridge/toshiba,tc358775.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Toshiba TC358775 DSI to LVDS bridge bindings
maintainers:
- Vinay Simha BN <simhavcs@gmail.com>
description: |
This binding supports DSI to LVDS bridge TC358775
MIPI DSI-RX Data 4-lane, CLK 1-lane with data rates up to 800 Mbps/lane.
Video frame size:
Up to 1600x1200 24-bit/pixel resolution for single-link LVDS display panel
limited by 135 MHz LVDS speed
Up to WUXGA (1920x1200 24-bit pixels) resolution for dual-link LVDS display
panel, limited by 270 MHz LVDS speed.
properties:
compatible:
const: toshiba,tc358775
reg:
maxItems: 1
description: i2c address of the bridge, 0x0f
vdd-supply:
maxItems: 1
description: 1.2V LVDS Power Supply
vddio-supply:
maxItems: 1
description: 1.8V IO Power Supply
stby-gpios:
maxItems: 1
description: Standby pin, Low active
reset-gpios:
maxItems: 1
description: Hardware reset, Low active
ports:
type: object
description:
A node containing input and output port nodes with endpoint definitions
as documented in
Documentation/devicetree/bindings/media/video-interfaces.txt
properties:
"#address-cells":
const: 1
"#size-cells":
const: 0
port@0:
type: object
description: |
DSI Input. The remote endpoint phandle should be a
reference to a valid mipi_dsi_host device node.
port@1:
type: object
description: |
Video port for LVDS output (panel or connector).
port@2:
type: object
description: |
Video port for Dual link LVDS output (panel or connector).
required:
- port@0
- port@1
required:
- compatible
- reg
- vdd-supply
- vddio-supply
- stby-gpios
- reset-gpios
- ports
examples:
- |
#include <dt-bindings/gpio/gpio.h>
/* For single-link LVDS display panel */
i2c@78b8000 {
/* On High speed expansion */
label = "HS-I2C2";
reg = <0x078b8000 0x500>;
clock-frequency = <400000>; /* fastmode operation */
#address-cells = <1>;
#size-cells = <0>;
tc_bridge: bridge@f {
compatible = "toshiba,tc358775";
reg = <0x0f>;
vdd-supply = <&pm8916_l2>;
vddio-supply = <&pm8916_l6>;
stby-gpios = <&msmgpio 99 GPIO_ACTIVE_LOW>;
reset-gpios = <&msmgpio 72 GPIO_ACTIVE_LOW>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
d2l_in_test: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
port@1 {
reg = <1>;
lvds_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
};
};
};
dsi@1a98000 {
reg = <0x1a98000 0x25c>;
reg-names = "dsi_ctrl";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
reg = <1>;
dsi0_out: endpoint {
remote-endpoint = <&d2l_in_test>;
data-lanes = <0 1 2 3>;
};
};
};
};
- |
/* For dual-link LVDS display panel */
i2c@78b8000 {
/* On High speed expansion */
label = "HS-I2C2";
reg = <0x078b8000 0x500>;
clock-frequency = <400000>; /* fastmode operation */
#address-cells = <1>;
#size-cells = <0>;
tc_bridge_dual: bridge@f {
compatible = "toshiba,tc358775";
reg = <0x0f>;
vdd-supply = <&pm8916_l2>;
vddio-supply = <&pm8916_l6>;
stby-gpios = <&msmgpio 99 GPIO_ACTIVE_LOW>;
reset-gpios = <&msmgpio 72 GPIO_ACTIVE_LOW>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
d2l_in_dual: endpoint {
remote-endpoint = <&dsi0_out_dual>;
};
};
port@1 {
reg = <1>;
lvds0_out: endpoint {
remote-endpoint = <&panel_in0>;
};
};
port@2 {
reg = <2>;
lvds1_out: endpoint {
remote-endpoint = <&panel_in1>;
};
};
};
};
};
dsi@1a98000 {
reg = <0x1a98000 0x25c>;
reg-names = "dsi_ctrl";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
reg = <1>;
dsi0_out_dual: endpoint {
remote-endpoint = <&d2l_in_dual>;
data-lanes = <0 1 2 3>;
};
};
};
};
...

View file

@ -13,7 +13,9 @@ properties:
compatible:
items:
- enum:
- bananapi,lhr050h41
- bananapi,lhr050h41
- feixin,k101-im2byl02
- const: ilitek,ili9881c
backlight: true

View file

@ -0,0 +1,70 @@
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/mantix,mlaf057we51-x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Mantix MLAF057WE51-X 5.7" 720x1440 TFT LCD panel
maintainers:
- Guido Günther <agx@sigxcpu.org>
description:
Mantix MLAF057WE51 X is a 720x1440 TFT LCD panel connected using
a MIPI-DSI video interface.
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
enum:
- mantix,mlaf057we51-x
port: true
reg:
maxItems: 1
description: DSI virtual channel
avdd-supply:
description: Positive analog power supply
avee-supply:
description: Negative analog power supply
vddi-supply:
description: 1.8V I/O voltage supply
reset-gpios: true
backlight: true
required:
- compatible
- reg
- avdd-supply
- avee-supply
- vddi-supply
- reset-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "mantix,mlaf057we51-x";
reg = <0>;
avdd-supply = <&reg_avdd>;
avee-supply = <&reg_avee>;
vddi-supply = <&reg_1v8_p>;
reset-gpios = <&gpio1 29 GPIO_ACTIVE_LOW>;
backlight = <&backlight>;
};
};
...

View file

@ -87,6 +87,8 @@ properties:
- cdtech,s070swv29hg-dc44
# CDTech(H.K.) Electronics Limited 7" 800x480 color TFT-LCD panel
- cdtech,s070wv95-ct16
# Chefree CH101OLHLWH-002 10.1" (1280x800) color TFT LCD panel
- chefree,ch101olhlwh-002
# Chunghwa Picture Tubes Ltd. 7" WXGA TFT LCD panel
- chunghwa,claa070wp03xg
# Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel
@ -159,6 +161,8 @@ properties:
- innolux,n156bge-l21
# Innolux Corporation 7.0" WSVGA (1024x600) TFT LCD panel
- innolux,zj070na-01p
# King & Display KD116N21-30NV-A010 eDP TFT LCD panel
- kingdisplay,kd116n21-30nv-a010
# Kaohsiung Opto-Electronics Inc. 5.7" QVGA (320 x 240) TFT LCD panel
- koe,tx14d24vm1bpa
# Kaohsiung Opto-Electronics Inc. 10.1" WUXGA (1920 x 1200) LVDS TFT LCD panel
@ -219,6 +223,8 @@ properties:
- osddisplays,osd070t1718-19ts
# One Stop Displays OSD101T2045-53TS 10.1" 1920x1200 panel
- osddisplays,osd101t2045-53ts
# POWERTIP PH800480T013-IDF2 7.0" WVGA TFT LCD panel
- powertip,ph800480t013-idf02
# QiaoDian XianShi Corporation 4"3 TFT LCD panel
- qiaodian,qd43003c0-40
# Rocktech Displays Ltd. RK101II01D-CT 10.1" TFT 1280x800

View file

@ -8,10 +8,11 @@ title: Rocktech JH057N00900 5.5" 720x1440 TFT LCD panel
maintainers:
- Ondrej Jirman <megi@xff.cz>
- Guido Gŭnther <agx@sigxcpu.org>
description: |
Rocktech JH057N00900 is a 720x1440 TFT LCD panel
connected using a MIPI-DSI video interface.
description:
Rocktech JH057N00900 is a 720x1440 TFT LCD panel
connected using a MIPI-DSI video interface.
allOf:
- $ref: panel-common.yaml#
@ -19,9 +20,9 @@ allOf:
properties:
compatible:
enum:
# Rocktech JH057N00900 5.5" 720x1440 TFT LCD panel
# Rocktech JH057N00900 5.5" 720x1440 TFT LCD panel
- rocktech,jh057n00900
# Xingbangda XBD599 5.99" 720x1440 TFT LCD panel
# Xingbangda XBD599 5.99" 720x1440 TFT LCD panel
- xingbangda,xbd599
port: true
@ -35,13 +36,9 @@ properties:
iovcc-supply:
description: I/O voltage supply
reset-gpios:
description: GPIO used for the reset pin
maxItems: 1
reset-gpios: true
backlight:
description: Backlight used by the panel
$ref: "/schemas/types.yaml#/definitions/phandle"
backlight: true
required:
- compatible
@ -57,15 +54,16 @@ examples:
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "rocktech,jh057n00900";
reg = <0>;
vcc-supply = <&reg_2v8_p>;
iovcc-supply = <&reg_1v8_p>;
reset-gpios = <&gpio3 13 GPIO_ACTIVE_LOW>;
backlight = <&backlight>;
};
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "rocktech,jh057n00900";
reg = <0>;
vcc-supply = <&reg_2v8_p>;
iovcc-supply = <&reg_1v8_p>;
reset-gpios = <&gpio3 13 GPIO_ACTIVE_LOW>;
backlight = <&backlight>;
};
};
...

View file

@ -197,6 +197,8 @@ patternProperties:
description: Ceva, Inc.
"^checkpoint,.*":
description: Check Point Software Technologies Ltd.
"^chefree,.*":
description: Chefree Technology Corp.
"^chipidea,.*":
description: Chipidea, Inc
"^chipone,.*":
@ -601,6 +603,8 @@ patternProperties:
description: Logic Technologies Limited
"^longcheer,.*":
description: Longcheer Technology (Shanghai) Co., Ltd.
"^lontium,.*":
description: Lontium Semiconductor Corporation
"^loongson,.*":
description: Loongson Technology Corporation Limited
"^lsi,.*":
@ -611,6 +615,8 @@ patternProperties:
description: Linux Automation GmbH
"^macnica,.*":
description: Macnica Americas
"^mantix,.*":
description: Mantix Display Technology Co.,Ltd.
"^mapleboard,.*":
description: Mapleboard.org
"^marvell,.*":
@ -830,6 +836,8 @@ patternProperties:
description: Poslab Technology Co., Ltd.
"^pov,.*":
description: Point of View International B.V.
"^powertip,.*":
description: Powertip Tech. Corp.
"^powervr,.*":
description: PowerVR (deprecated, use img)
"^primux,.*":

View file

@ -1,3 +1,5 @@
.. Copyright 2020 DisplayLink (UK) Ltd.
===================
Userland interfaces
===================
@ -162,6 +164,116 @@ other hand, a driver requires shared state between clients which is
visible to user-space and accessible beyond open-file boundaries, they
cannot support render nodes.
Device Hot-Unplug
=================
.. note::
The following is the plan. Implementation is not there yet
(2020 May).
Graphics devices (display and/or render) may be connected via USB (e.g.
display adapters or docking stations) or Thunderbolt (e.g. eGPU). An end
user is able to hot-unplug this kind of devices while they are being
used, and expects that the very least the machine does not crash. Any
damage from hot-unplugging a DRM device needs to be limited as much as
possible and userspace must be given the chance to handle it if it wants
to. Ideally, unplugging a DRM device still lets a desktop continue to
run, but that is going to need explicit support throughout the whole
graphics stack: from kernel and userspace drivers, through display
servers, via window system protocols, and in applications and libraries.
Other scenarios that should lead to the same are: unrecoverable GPU
crash, PCI device disappearing off the bus, or forced unbind of a driver
from the physical device.
In other words, from userspace perspective everything needs to keep on
working more or less, until userspace stops using the disappeared DRM
device and closes it completely. Userspace will learn of the device
disappearance from the device removed uevent, ioctls returning ENODEV
(or driver-specific ioctls returning driver-specific things), or open()
returning ENXIO.
Only after userspace has closed all relevant DRM device and dmabuf file
descriptors and removed all mmaps, the DRM driver can tear down its
instance for the device that no longer exists. If the same physical
device somehow comes back in the mean time, it shall be a new DRM
device.
Similar to PIDs, chardev minor numbers are not recycled immediately. A
new DRM device always picks the next free minor number compared to the
previous one allocated, and wraps around when minor numbers are
exhausted.
The goal raises at least the following requirements for the kernel and
drivers.
Requirements for KMS UAPI
-------------------------
- KMS connectors must change their status to disconnected.
- Legacy modesets and pageflips, and atomic commits, both real and
TEST_ONLY, and any other ioctls either fail with ENODEV or fake
success.
- Pending non-blocking KMS operations deliver the DRM events userspace
is expecting. This applies also to ioctls that faked success.
- open() on a device node whose underlying device has disappeared will
fail with ENXIO.
- Attempting to create a DRM lease on a disappeared DRM device will
fail with ENODEV. Existing DRM leases remain and work as listed
above.
Requirements for Render and Cross-Device UAPI
---------------------------------------------
- All GPU jobs that can no longer run must have their fences
force-signalled to avoid inflicting hangs on userspace.
The associated error code is ENODEV.
- Some userspace APIs already define what should happen when the device
disappears (OpenGL, GL ES: `GL_KHR_robustness`_; `Vulkan`_:
VK_ERROR_DEVICE_LOST; etc.). DRM drivers are free to implement this
behaviour the way they see best, e.g. returning failures in
driver-specific ioctls and handling those in userspace drivers, or
rely on uevents, and so on.
- dmabuf which point to memory that has disappeared will either fail to
import with ENODEV or continue to be successfully imported if it would
have succeeded before the disappearance. See also about memory maps
below for already imported dmabufs.
- Attempting to import a dmabuf to a disappeared device will either fail
with ENODEV or succeed if it would have succeeded without the
disappearance.
- open() on a device node whose underlying device has disappeared will
fail with ENXIO.
.. _GL_KHR_robustness: https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_robustness.txt
.. _Vulkan: https://www.khronos.org/vulkan/
Requirements for Memory Maps
----------------------------
Memory maps have further requirements that apply to both existing maps
and maps created after the device has disappeared. If the underlying
memory disappears, the map is created or modified such that reads and
writes will still complete successfully but the result is undefined.
This applies to both userspace mmap()'d memory and memory pointed to by
dmabuf which might be mapped to other devices (cross-device dmabuf
imports).
Raising SIGBUS is not an option, because userspace cannot realistically
handle it. Signal handlers are global, which makes them extremely
difficult to use correctly from libraries like those that Mesa produces.
Signal handlers are not composable, you can't have different handlers
for GPU1 and GPU2 from different vendors, and a third handler for
mmapped regular files. Threads cause additional pain with signal
handling as well.
.. _drm_driver_ioctl:
IOCTL Support on Device Nodes
@ -199,7 +311,7 @@ EPERM/EACCES:
difference between EACCES and EPERM.
ENODEV:
The device is not (yet) present or fully initialized.
The device is not present anymore or is not yet fully initialized.
EOPNOTSUPP:
Feature (like PRIME, modesetting, GEM) is not supported by the driver.

View file

@ -1,6 +1,6 @@
==========================================
drm/pl111 ARM PrimeCell PL111 CLCD Driver
==========================================
====================================================
drm/pl111 ARM PrimeCell PL110 and PL111 CLCD Driver
====================================================
.. kernel-doc:: drivers/gpu/drm/pl111/pl111_drv.c
:doc: ARM PrimeCell PL111 CLCD Driver
:doc: ARM PrimeCell PL110 and PL111 CLCD Driver

View file

@ -403,6 +403,52 @@ Contact: Emil Velikov, respective driver maintainers
Level: Intermediate
Plumb drm_atomic_state all over
-------------------------------
Currently various atomic functions take just a single or a handful of
object states (eg. plane state). While that single object state can
suffice for some simple cases, we often have to dig out additional
object states for dealing with various dependencies between the individual
objects or the hardware they represent. The process of digging out the
additional states is rather non-intuitive and error prone.
To fix that most functions should rather take the overall
drm_atomic_state as one of their parameters. The other parameters
would generally be the object(s) we mainly want to interact with.
For example, instead of
.. code-block:: c
int (*atomic_check)(struct drm_plane *plane, struct drm_plane_state *state);
we would have something like
.. code-block:: c
int (*atomic_check)(struct drm_plane *plane, struct drm_atomic_state *state);
The implementation can then trivially gain access to any required object
state(s) via drm_atomic_get_plane_state(), drm_atomic_get_new_plane_state(),
drm_atomic_get_old_plane_state(), and their equivalents for
other object types.
Additionally many drivers currently access the object->state pointer
directly in their commit functions. That is not going to work if we
eg. want to allow deeper commit pipelines as those pointers could
then point to the states corresponding to a future commit instead of
the current commit we're trying to process. Also non-blocking commits
execute locklessly so there are serious concerns with dereferencing
the object->state pointers without holding the locks that protect them.
Use of drm_atomic_get_new_plane_state(), drm_atomic_get_old_plane_state(),
etc. avoids these problems as well since they relate to a specific
commit via the passed in drm_atomic_state.
Contact: Ville Syrjälä, Daniel Vetter
Level: Intermediate
Core refactorings
=================

View file

@ -5474,12 +5474,19 @@ S: Maintained
F: drivers/gpu/drm/panel/panel-lvds.c
F: Documentation/devicetree/bindings/display/panel/lvds.yaml
DRM DRIVER FOR MANTIX MLAF057WE51 PANELS
M: Guido Günther <agx@sigxcpu.org>
R: Purism Kernel Team <kernel@puri.sm>
S: Maintained
F: Documentation/devicetree/bindings/display/panel/mantix,mlaf057we51-x.yaml
F: drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c
DRM DRIVER FOR MATROX G200/G400 GRAPHICS CARDS
S: Orphan / Obsolete
F: drivers/gpu/drm/mga/
F: include/uapi/drm/mga_drm.h
DRM DRIVER FOR MGA G200 SERVER GRAPHICS CHIPS
DRM DRIVER FOR MGA G200 GRAPHICS CHIPS
M: Dave Airlie <airlied@redhat.com>
S: Odd Fixes
F: drivers/gpu/drm/mgag200/
@ -5767,7 +5774,7 @@ F: drivers/gpu/drm/gma500/
DRM DRIVERS FOR HISILICON
M: Xinliang Liu <xinliang.liu@linaro.org>
M: Rongrong Zou <zourongrong@gmail.com>
M: Tian Tao <tiantao6@hisilicon.com>
R: John Stultz <john.stultz@linaro.org>
R: Xinwei Kong <kong.kongxinwei@hisilicon.com>
R: Chen Feng <puck.chen@hisilicon.com>

View file

@ -517,8 +517,9 @@ int amdgpu_amdkfd_get_dmabuf_info(struct kgd_dev *kgd, int dma_buf_fd,
uint64_t amdgpu_amdkfd_get_vram_usage(struct kgd_dev *kgd)
{
struct amdgpu_device *adev = (struct amdgpu_device *)kgd;
struct ttm_resource_manager *vram_man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
return amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
return amdgpu_vram_mgr_usage(vram_man);
}
uint64_t amdgpu_amdkfd_get_hive_id(struct kgd_dev *kgd)

View file

@ -562,7 +562,7 @@ static int init_user_pages(struct kgd_mem *mem, uint64_t user_addr)
mutex_lock(&process_info->lock);
ret = amdgpu_ttm_tt_set_userptr(bo->tbo.ttm, user_addr, 0);
ret = amdgpu_ttm_tt_set_userptr(&bo->tbo, user_addr, 0);
if (ret) {
pr_err("%s: Failed to set userptr: %d\n", __func__, ret);
goto out;

View file

@ -26,6 +26,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/amdgpu_drm.h>
#include "amdgpu.h"
@ -1413,6 +1414,10 @@ amdgpu_connector_dp_detect(struct drm_connector *connector, bool force)
pm_runtime_put_autosuspend(connector->dev->dev);
}
drm_dp_set_subconnector_property(&amdgpu_connector->base,
ret,
amdgpu_dig_connector->dpcd,
amdgpu_dig_connector->downstream_ports);
return ret;
}
@ -1959,6 +1964,11 @@ amdgpu_connector_add(struct amdgpu_device *adev,
if (has_aux)
amdgpu_atombios_dp_aux_init(amdgpu_connector);
if (connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
connector_type == DRM_MODE_CONNECTOR_eDP) {
drm_connector_attach_dp_subconnector_property(&amdgpu_connector->base);
}
return;
failed:

View file

@ -299,7 +299,7 @@ static void amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev,
{
s64 time_us, increment_us;
u64 free_vram, total_vram, used_vram;
struct ttm_resource_manager *vram_man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
/* Allow a maximum of 200 accumulated ms. This is basically per-IB
* throttling.
*
@ -316,7 +316,7 @@ static void amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev,
}
total_vram = adev->gmc.real_vram_size - atomic64_read(&adev->vram_pin_size);
used_vram = amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
used_vram = amdgpu_vram_mgr_usage(vram_man);
free_vram = used_vram >= total_vram ? 0 : total_vram - used_vram;
spin_lock(&adev->mm_stats.lock);
@ -363,7 +363,7 @@ static void amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev,
if (!amdgpu_gmc_vram_full_visible(&adev->gmc)) {
u64 total_vis_vram = adev->gmc.visible_vram_size;
u64 used_vis_vram =
amdgpu_vram_mgr_vis_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
amdgpu_vram_mgr_vis_usage(vram_man);
if (used_vis_vram < total_vis_vram) {
u64 free_vis_vram = total_vis_vram - used_vis_vram;

View file

@ -3882,7 +3882,7 @@ static int amdgpu_device_reset_sriov(struct amdgpu_device *adev,
amdgpu_virt_init_data_exchange(adev);
/* we need recover gart prior to run SMC/CP/SDMA resume */
amdgpu_gtt_mgr_recover(&adev->mman.bdev.man[TTM_PL_TT]);
amdgpu_gtt_mgr_recover(ttm_manager_type(&adev->mman.bdev, TTM_PL_TT));
r = amdgpu_device_fw_loading(adev);
if (r)
@ -4081,8 +4081,7 @@ static int amdgpu_do_asic_reset(struct amdgpu_hive_info *hive,
amdgpu_inc_vram_lost(tmp_adev);
}
r = amdgpu_gtt_mgr_recover(
&tmp_adev->mman.bdev.man[TTM_PL_TT]);
r = amdgpu_gtt_mgr_recover(ttm_manager_type(&tmp_adev->mman.bdev, TTM_PL_TT));
if (r)
goto out;

View file

@ -393,12 +393,12 @@ MODULE_PARM_DESC(sched_hw_submission, "the max number of HW submissions (default
module_param_named(sched_hw_submission, amdgpu_sched_hw_submission, int, 0444);
/**
* DOC: ppfeaturemask (uint)
* DOC: ppfeaturemask (hexint)
* Override power features enabled. See enum PP_FEATURE_MASK in drivers/gpu/drm/amd/include/amd_shared.h.
* The default is the current set of stable power features.
*/
MODULE_PARM_DESC(ppfeaturemask, "all power features enabled (default))");
module_param_named(ppfeaturemask, amdgpu_pp_feature_mask, uint, 0444);
module_param_named(ppfeaturemask, amdgpu_pp_feature_mask, hexint, 0444);
/**
* DOC: forcelongtraining (uint)

View file

@ -332,7 +332,7 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data,
bo = gem_to_amdgpu_bo(gobj);
bo->preferred_domains = AMDGPU_GEM_DOMAIN_GTT;
bo->allowed_domains = AMDGPU_GEM_DOMAIN_GTT;
r = amdgpu_ttm_tt_set_userptr(bo->tbo.ttm, args->addr, args->flags);
r = amdgpu_ttm_tt_set_userptr(&bo->tbo, args->addr, args->flags);
if (r)
goto release_object;

View file

@ -24,11 +24,10 @@
#include "amdgpu.h"
struct amdgpu_gtt_mgr {
struct drm_mm mm;
spinlock_t lock;
atomic64_t available;
};
static inline struct amdgpu_gtt_mgr *to_gtt_mgr(struct ttm_resource_manager *man)
{
return container_of(man, struct amdgpu_gtt_mgr, manager);
}
struct amdgpu_gtt_node {
struct drm_mm_node node;
@ -48,9 +47,9 @@ static ssize_t amdgpu_mem_info_gtt_total_show(struct device *dev,
{
struct drm_device *ddev = dev_get_drvdata(dev);
struct amdgpu_device *adev = ddev->dev_private;
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT);
return snprintf(buf, PAGE_SIZE, "%llu\n",
(adev->mman.bdev.man[TTM_PL_TT].size) * PAGE_SIZE);
man->size * PAGE_SIZE);
}
/**
@ -66,9 +65,9 @@ static ssize_t amdgpu_mem_info_gtt_used_show(struct device *dev,
{
struct drm_device *ddev = dev_get_drvdata(dev);
struct amdgpu_device *adev = ddev->dev_private;
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT);
return snprintf(buf, PAGE_SIZE, "%llu\n",
amdgpu_gtt_mgr_usage(&adev->mman.bdev.man[TTM_PL_TT]));
amdgpu_gtt_mgr_usage(man));
}
static DEVICE_ATTR(mem_info_gtt_total, S_IRUGO,
@ -76,6 +75,7 @@ static DEVICE_ATTR(mem_info_gtt_total, S_IRUGO,
static DEVICE_ATTR(mem_info_gtt_used, S_IRUGO,
amdgpu_mem_info_gtt_used_show, NULL);
static const struct ttm_resource_manager_func amdgpu_gtt_mgr_func;
/**
* amdgpu_gtt_mgr_init - init GTT manager and DRM MM
*
@ -84,24 +84,25 @@ static DEVICE_ATTR(mem_info_gtt_used, S_IRUGO,
*
* Allocate and initialize the GTT manager.
*/
static int amdgpu_gtt_mgr_init(struct ttm_mem_type_manager *man,
unsigned long p_size)
int amdgpu_gtt_mgr_init(struct amdgpu_device *adev, uint64_t gtt_size)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
struct amdgpu_gtt_mgr *mgr;
struct amdgpu_gtt_mgr *mgr = &adev->mman.gtt_mgr;
struct ttm_resource_manager *man = &mgr->manager;
uint64_t start, size;
int ret;
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
if (!mgr)
return -ENOMEM;
man->use_tt = true;
man->func = &amdgpu_gtt_mgr_func;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
ttm_resource_manager_init(man, gtt_size >> PAGE_SHIFT);
start = AMDGPU_GTT_MAX_TRANSFER_SIZE * AMDGPU_GTT_NUM_TRANSFER_WINDOWS;
size = (adev->gmc.gart_size >> PAGE_SHIFT) - start;
drm_mm_init(&mgr->mm, start, size);
spin_lock_init(&mgr->lock);
atomic64_set(&mgr->available, p_size);
man->priv = mgr;
atomic64_set(&mgr->available, gtt_size >> PAGE_SHIFT);
ret = device_create_file(adev->dev, &dev_attr_mem_info_gtt_total);
if (ret) {
@ -114,6 +115,8 @@ static int amdgpu_gtt_mgr_init(struct ttm_mem_type_manager *man,
return ret;
}
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_TT, &mgr->manager);
ttm_resource_manager_set_used(man, true);
return 0;
}
@ -125,20 +128,27 @@ static int amdgpu_gtt_mgr_init(struct ttm_mem_type_manager *man,
* Destroy and free the GTT manager, returns -EBUSY if ranges are still
* allocated inside it.
*/
static int amdgpu_gtt_mgr_fini(struct ttm_mem_type_manager *man)
void amdgpu_gtt_mgr_fini(struct amdgpu_device *adev)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
struct amdgpu_gtt_mgr *mgr = man->priv;
struct amdgpu_gtt_mgr *mgr = &adev->mman.gtt_mgr;
struct ttm_resource_manager *man = &mgr->manager;
int ret;
ttm_resource_manager_set_used(man, false);
ret = ttm_resource_manager_force_list_clean(&adev->mman.bdev, man);
if (ret)
return;
spin_lock(&mgr->lock);
drm_mm_takedown(&mgr->mm);
spin_unlock(&mgr->lock);
kfree(mgr);
man->priv = NULL;
device_remove_file(adev->dev, &dev_attr_mem_info_gtt_total);
device_remove_file(adev->dev, &dev_attr_mem_info_gtt_used);
return 0;
ttm_resource_manager_cleanup(man);
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_TT, NULL);
}
/**
@ -148,7 +158,7 @@ static int amdgpu_gtt_mgr_fini(struct ttm_mem_type_manager *man)
*
* Check if a mem object has already address space allocated.
*/
bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_mem_reg *mem)
bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_resource *mem)
{
return mem->mm_node != NULL;
}
@ -163,12 +173,12 @@ bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_mem_reg *mem)
*
* Dummy, allocate the node but no space for it yet.
*/
static int amdgpu_gtt_mgr_new(struct ttm_mem_type_manager *man,
static int amdgpu_gtt_mgr_new(struct ttm_resource_manager *man,
struct ttm_buffer_object *tbo,
const struct ttm_place *place,
struct ttm_mem_reg *mem)
struct ttm_resource *mem)
{
struct amdgpu_gtt_mgr *mgr = man->priv;
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
struct amdgpu_gtt_node *node;
int r;
@ -226,10 +236,10 @@ static int amdgpu_gtt_mgr_new(struct ttm_mem_type_manager *man,
*
* Free the allocated GTT again.
*/
static void amdgpu_gtt_mgr_del(struct ttm_mem_type_manager *man,
struct ttm_mem_reg *mem)
static void amdgpu_gtt_mgr_del(struct ttm_resource_manager *man,
struct ttm_resource *mem)
{
struct amdgpu_gtt_mgr *mgr = man->priv;
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
struct amdgpu_gtt_node *node = mem->mm_node;
if (node) {
@ -249,17 +259,17 @@ static void amdgpu_gtt_mgr_del(struct ttm_mem_type_manager *man,
*
* Return how many bytes are used in the GTT domain
*/
uint64_t amdgpu_gtt_mgr_usage(struct ttm_mem_type_manager *man)
uint64_t amdgpu_gtt_mgr_usage(struct ttm_resource_manager *man)
{
struct amdgpu_gtt_mgr *mgr = man->priv;
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
s64 result = man->size - atomic64_read(&mgr->available);
return (result > 0 ? result : 0) * PAGE_SIZE;
}
int amdgpu_gtt_mgr_recover(struct ttm_mem_type_manager *man)
int amdgpu_gtt_mgr_recover(struct ttm_resource_manager *man)
{
struct amdgpu_gtt_mgr *mgr = man->priv;
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
struct amdgpu_gtt_node *node;
struct drm_mm_node *mm_node;
int r = 0;
@ -284,10 +294,10 @@ int amdgpu_gtt_mgr_recover(struct ttm_mem_type_manager *man)
*
* Dump the table content using printk.
*/
static void amdgpu_gtt_mgr_debug(struct ttm_mem_type_manager *man,
static void amdgpu_gtt_mgr_debug(struct ttm_resource_manager *man,
struct drm_printer *printer)
{
struct amdgpu_gtt_mgr *mgr = man->priv;
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
spin_lock(&mgr->lock);
drm_mm_print(&mgr->mm, printer);
@ -298,10 +308,8 @@ static void amdgpu_gtt_mgr_debug(struct ttm_mem_type_manager *man,
amdgpu_gtt_mgr_usage(man) >> 20);
}
const struct ttm_mem_type_manager_func amdgpu_gtt_mgr_func = {
.init = amdgpu_gtt_mgr_init,
.takedown = amdgpu_gtt_mgr_fini,
.get_node = amdgpu_gtt_mgr_new,
.put_node = amdgpu_gtt_mgr_del,
static const struct ttm_resource_manager_func amdgpu_gtt_mgr_func = {
.alloc = amdgpu_gtt_mgr_new,
.free = amdgpu_gtt_mgr_del,
.debug = amdgpu_gtt_mgr_debug
};

View file

@ -594,13 +594,13 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
ui64 = atomic64_read(&adev->num_vram_cpu_page_faults);
return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
case AMDGPU_INFO_VRAM_USAGE:
ui64 = amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
ui64 = amdgpu_vram_mgr_usage(ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM));
return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
case AMDGPU_INFO_VIS_VRAM_USAGE:
ui64 = amdgpu_vram_mgr_vis_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
ui64 = amdgpu_vram_mgr_vis_usage(ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM));
return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
case AMDGPU_INFO_GTT_USAGE:
ui64 = amdgpu_gtt_mgr_usage(&adev->mman.bdev.man[TTM_PL_TT]);
ui64 = amdgpu_gtt_mgr_usage(ttm_manager_type(&adev->mman.bdev, TTM_PL_TT));
return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
case AMDGPU_INFO_GDS_CONFIG: {
struct drm_amdgpu_info_gds gds_info;
@ -623,7 +623,7 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
min(adev->gmc.visible_vram_size -
atomic64_read(&adev->visible_pin_size),
vram_gtt.vram_size);
vram_gtt.gtt_size = adev->mman.bdev.man[TTM_PL_TT].size;
vram_gtt.gtt_size = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT)->size;
vram_gtt.gtt_size *= PAGE_SIZE;
vram_gtt.gtt_size -= atomic64_read(&adev->gart_pin_size);
return copy_to_user(out, &vram_gtt,
@ -631,14 +631,17 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
}
case AMDGPU_INFO_MEMORY: {
struct drm_amdgpu_memory_info mem;
struct ttm_resource_manager *vram_man =
ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
struct ttm_resource_manager *gtt_man =
ttm_manager_type(&adev->mman.bdev, TTM_PL_TT);
memset(&mem, 0, sizeof(mem));
mem.vram.total_heap_size = adev->gmc.real_vram_size;
mem.vram.usable_heap_size = adev->gmc.real_vram_size -
atomic64_read(&adev->vram_pin_size) -
AMDGPU_VM_RESERVED_VRAM;
mem.vram.heap_usage =
amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
amdgpu_vram_mgr_usage(vram_man);
mem.vram.max_allocation = mem.vram.usable_heap_size * 3 / 4;
mem.cpu_accessible_vram.total_heap_size =
@ -648,16 +651,16 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
atomic64_read(&adev->visible_pin_size),
mem.vram.usable_heap_size);
mem.cpu_accessible_vram.heap_usage =
amdgpu_vram_mgr_vis_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
amdgpu_vram_mgr_vis_usage(vram_man);
mem.cpu_accessible_vram.max_allocation =
mem.cpu_accessible_vram.usable_heap_size * 3 / 4;
mem.gtt.total_heap_size = adev->mman.bdev.man[TTM_PL_TT].size;
mem.gtt.total_heap_size = gtt_man->size;
mem.gtt.total_heap_size *= PAGE_SIZE;
mem.gtt.usable_heap_size = mem.gtt.total_heap_size -
atomic64_read(&adev->gart_pin_size);
mem.gtt.heap_usage =
amdgpu_gtt_mgr_usage(&adev->mman.bdev.man[TTM_PL_TT]);
amdgpu_gtt_mgr_usage(gtt_man);
mem.gtt.max_allocation = mem.gtt.usable_heap_size * 3 / 4;
return copy_to_user(out, &mem,

View file

@ -469,6 +469,7 @@ struct amdgpu_encoder {
struct amdgpu_connector_atom_dig {
/* displayport */
u8 dpcd[DP_RECEIVER_CAP_SIZE];
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
u8 dp_sink_type;
int dp_clock;
int dp_lane_count;

View file

@ -381,7 +381,7 @@ int amdgpu_bo_create_kernel_at(struct amdgpu_device *adev,
if (cpu_addr)
amdgpu_bo_kunmap(*bo_ptr);
ttm_bo_mem_put(&(*bo_ptr)->tbo, &(*bo_ptr)->tbo.mem);
ttm_resource_free(&(*bo_ptr)->tbo, &(*bo_ptr)->tbo.mem);
for (i = 0; i < (*bo_ptr)->placement.num_placement; ++i) {
(*bo_ptr)->placements[i].fpfn = offset >> PAGE_SHIFT;
@ -442,14 +442,14 @@ void amdgpu_bo_free_kernel(struct amdgpu_bo **bo, u64 *gpu_addr,
static bool amdgpu_bo_validate_size(struct amdgpu_device *adev,
unsigned long size, u32 domain)
{
struct ttm_mem_type_manager *man = NULL;
struct ttm_resource_manager *man = NULL;
/*
* If GTT is part of requested domains the check must succeed to
* allow fall back to GTT
*/
if (domain & AMDGPU_GEM_DOMAIN_GTT) {
man = &adev->mman.bdev.man[TTM_PL_TT];
man = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT);
if (size < (man->size << PAGE_SHIFT))
return true;
@ -458,7 +458,7 @@ static bool amdgpu_bo_validate_size(struct amdgpu_device *adev,
}
if (domain & AMDGPU_GEM_DOMAIN_VRAM) {
man = &adev->mman.bdev.man[TTM_PL_VRAM];
man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
if (size < (man->size << PAGE_SHIFT))
return true;
@ -1268,11 +1268,11 @@ int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer,
*/
void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
bool evict,
struct ttm_mem_reg *new_mem)
struct ttm_resource *new_mem)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev);
struct amdgpu_bo *abo;
struct ttm_mem_reg *old_mem = &bo->mem;
struct ttm_resource *old_mem = &bo->mem;
if (!amdgpu_bo_is_amdgpu_bo(bo))
return;

View file

@ -160,7 +160,7 @@ static inline int amdgpu_bo_reserve(struct amdgpu_bo *bo, bool no_intr)
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
int r;
r = __ttm_bo_reserve(&bo->tbo, !no_intr, false, NULL);
r = ttm_bo_reserve(&bo->tbo, !no_intr, false, NULL);
if (unlikely(r != 0)) {
if (r != -ERESTARTSYS)
dev_err(adev->dev, "%p reserve failed\n", bo);
@ -283,7 +283,7 @@ int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer,
uint64_t *flags);
void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
bool evict,
struct ttm_mem_reg *new_mem);
struct ttm_resource *new_mem);
void amdgpu_bo_release_notify(struct ttm_buffer_object *bo);
int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
void amdgpu_bo_fence(struct amdgpu_bo *bo, struct dma_fence *fence,

View file

@ -63,61 +63,13 @@
#define AMDGPU_TTM_VRAM_MAX_DW_READ (size_t)128
/**
* amdgpu_init_mem_type - Initialize a memory manager for a specific type of
* memory request.
*
* @bdev: The TTM BO device object (contains a reference to amdgpu_device)
* @type: The type of memory requested
* @man: The memory type manager for each domain
*
* This is called by ttm_bo_init_mm() when a buffer object is being
* initialized.
*/
static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
struct ttm_mem_type_manager *man)
static int amdgpu_ttm_init_on_chip(struct amdgpu_device *adev,
unsigned int type,
uint64_t size)
{
struct amdgpu_device *adev;
adev = amdgpu_ttm_adev(bdev);
switch (type) {
case TTM_PL_SYSTEM:
/* System memory */
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_TT:
/* GTT memory */
man->func = &amdgpu_gtt_mgr_func;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
break;
case TTM_PL_VRAM:
/* "On-card" video ram */
man->func = &amdgpu_vram_mgr_func;
man->flags = TTM_MEMTYPE_FLAG_FIXED |
TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC;
man->default_caching = TTM_PL_FLAG_WC;
break;
case AMDGPU_PL_GDS:
case AMDGPU_PL_GWS:
case AMDGPU_PL_OA:
/* On-chip GDS memory*/
man->func = &ttm_bo_manager_func;
man->flags = TTM_MEMTYPE_FLAG_FIXED;
man->available_caching = TTM_PL_FLAG_UNCACHED;
man->default_caching = TTM_PL_FLAG_UNCACHED;
break;
default:
DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
return -EINVAL;
}
return 0;
return ttm_range_man_init(&adev->mman.bdev, type,
TTM_PL_FLAG_UNCACHED, TTM_PL_FLAG_UNCACHED,
false, size >> PAGE_SHIFT);
}
/**
@ -231,9 +183,9 @@ static int amdgpu_verify_access(struct ttm_buffer_object *bo, struct file *filp)
* Assign the memory from new_mem to the memory of the buffer object bo.
*/
static void amdgpu_move_null(struct ttm_buffer_object *bo,
struct ttm_mem_reg *new_mem)
struct ttm_resource *new_mem)
{
struct ttm_mem_reg *old_mem = &bo->mem;
struct ttm_resource *old_mem = &bo->mem;
BUG_ON(old_mem->mm_node != NULL);
*old_mem = *new_mem;
@ -250,7 +202,7 @@ static void amdgpu_move_null(struct ttm_buffer_object *bo,
*/
static uint64_t amdgpu_mm_node_addr(struct ttm_buffer_object *bo,
struct drm_mm_node *mm_node,
struct ttm_mem_reg *mem)
struct ttm_resource *mem)
{
uint64_t addr = 0;
@ -270,7 +222,7 @@ static uint64_t amdgpu_mm_node_addr(struct ttm_buffer_object *bo,
* @offset: The offset that drm_mm_node is used for finding.
*
*/
static struct drm_mm_node *amdgpu_find_mm_node(struct ttm_mem_reg *mem,
static struct drm_mm_node *amdgpu_find_mm_node(struct ttm_resource *mem,
uint64_t *offset)
{
struct drm_mm_node *mm_node = mem->mm_node;
@ -298,7 +250,7 @@ static struct drm_mm_node *amdgpu_find_mm_node(struct ttm_mem_reg *mem,
* the physical address for local memory.
*/
static int amdgpu_ttm_map_buffer(struct ttm_buffer_object *bo,
struct ttm_mem_reg *mem,
struct ttm_resource *mem,
struct drm_mm_node *mm_node,
unsigned num_pages, uint64_t offset,
unsigned window, struct amdgpu_ring *ring,
@ -522,8 +474,8 @@ int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev,
*/
static int amdgpu_move_blit(struct ttm_buffer_object *bo,
bool evict, bool no_wait_gpu,
struct ttm_mem_reg *new_mem,
struct ttm_mem_reg *old_mem)
struct ttm_resource *new_mem,
struct ttm_resource *old_mem)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev);
struct amdgpu_bo *abo = ttm_to_amdgpu_bo(bo);
@ -582,10 +534,10 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo,
*/
static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, bool evict,
struct ttm_operation_ctx *ctx,
struct ttm_mem_reg *new_mem)
struct ttm_resource *new_mem)
{
struct ttm_mem_reg *old_mem = &bo->mem;
struct ttm_mem_reg tmp_mem;
struct ttm_resource *old_mem = &bo->mem;
struct ttm_resource tmp_mem;
struct ttm_place placements;
struct ttm_placement placement;
int r;
@ -627,7 +579,7 @@ static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, bool evict,
/* move BO (in tmp_mem) to new_mem */
r = ttm_bo_move_ttm(bo, ctx, new_mem);
out_cleanup:
ttm_bo_mem_put(bo, &tmp_mem);
ttm_resource_free(bo, &tmp_mem);
return r;
}
@ -638,10 +590,10 @@ static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, bool evict,
*/
static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo, bool evict,
struct ttm_operation_ctx *ctx,
struct ttm_mem_reg *new_mem)
struct ttm_resource *new_mem)
{
struct ttm_mem_reg *old_mem = &bo->mem;
struct ttm_mem_reg tmp_mem;
struct ttm_resource *old_mem = &bo->mem;
struct ttm_resource tmp_mem;
struct ttm_placement placement;
struct ttm_place placements;
int r;
@ -674,7 +626,7 @@ static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo, bool evict,
goto out_cleanup;
}
out_cleanup:
ttm_bo_mem_put(bo, &tmp_mem);
ttm_resource_free(bo, &tmp_mem);
return r;
}
@ -684,7 +636,7 @@ static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo, bool evict,
* Called by amdgpu_bo_move()
*/
static bool amdgpu_mem_visible(struct amdgpu_device *adev,
struct ttm_mem_reg *mem)
struct ttm_resource *mem)
{
struct drm_mm_node *nodes = mem->mm_node;
@ -694,7 +646,7 @@ static bool amdgpu_mem_visible(struct amdgpu_device *adev,
if (mem->mem_type != TTM_PL_VRAM)
return false;
/* ttm_mem_reg_ioremap only supports contiguous memory */
/* ttm_resource_ioremap only supports contiguous memory */
if (nodes->size != mem->num_pages)
return false;
@ -709,11 +661,11 @@ static bool amdgpu_mem_visible(struct amdgpu_device *adev,
*/
static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
struct ttm_operation_ctx *ctx,
struct ttm_mem_reg *new_mem)
struct ttm_resource *new_mem)
{
struct amdgpu_device *adev;
struct amdgpu_bo *abo;
struct ttm_mem_reg *old_mem = &bo->mem;
struct ttm_resource *old_mem = &bo->mem;
int r;
/* Can't move a pinned BO */
@ -795,19 +747,12 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
*
* Called by ttm_mem_io_reserve() ultimately via ttm_bo_vm_fault()
*/
static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_resource *mem)
{
struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
struct amdgpu_device *adev = amdgpu_ttm_adev(bdev);
struct drm_mm_node *mm_node = mem->mm_node;
size_t bus_size = (size_t)mem->num_pages << PAGE_SHIFT;
mem->bus.addr = NULL;
mem->bus.offset = 0;
mem->bus.size = mem->num_pages << PAGE_SHIFT;
mem->bus.base = 0;
mem->bus.is_iomem = false;
if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
return -EINVAL;
switch (mem->mem_type) {
case TTM_PL_SYSTEM:
/* system memory */
@ -817,11 +762,11 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_
case TTM_PL_VRAM:
mem->bus.offset = mem->start << PAGE_SHIFT;
/* check if it's visible */
if ((mem->bus.offset + mem->bus.size) > adev->gmc.visible_vram_size)
if ((mem->bus.offset + bus_size) > adev->gmc.visible_vram_size)
return -EINVAL;
/* Only physically contiguous buffers apply. In a contiguous
* buffer, size of the first mm_node would match the number of
* pages in ttm_mem_reg.
* pages in ttm_resource.
*/
if (adev->mman.aper_base_kaddr &&
(mm_node->size == mem->num_pages))
@ -1166,7 +1111,7 @@ static int amdgpu_ttm_gart_bind(struct amdgpu_device *adev,
* This handles binding GTT memory to the device address space.
*/
static int amdgpu_ttm_backend_bind(struct ttm_tt *ttm,
struct ttm_mem_reg *bo_mem)
struct ttm_resource *bo_mem)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(ttm->bdev);
struct amdgpu_ttm_tt *gtt = (void*)ttm;
@ -1217,7 +1162,7 @@ int amdgpu_ttm_alloc_gart(struct ttm_buffer_object *bo)
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev);
struct ttm_operation_ctx ctx = { false, false };
struct amdgpu_ttm_tt *gtt = (void*)bo->ttm;
struct ttm_mem_reg tmp;
struct ttm_resource tmp;
struct ttm_placement placement;
struct ttm_place placements;
uint64_t addr, flags;
@ -1254,11 +1199,11 @@ int amdgpu_ttm_alloc_gart(struct ttm_buffer_object *bo)
gtt->offset = (u64)tmp.start << PAGE_SHIFT;
r = amdgpu_ttm_gart_bind(adev, bo, flags);
if (unlikely(r)) {
ttm_bo_mem_put(bo, &tmp);
ttm_resource_free(bo, &tmp);
return r;
}
ttm_bo_mem_put(bo, &bo->mem);
ttm_resource_free(bo, &bo->mem);
bo->mem = tmp;
}
@ -1457,21 +1402,26 @@ static void amdgpu_ttm_tt_unpopulate(struct ttm_tt *ttm)
* amdgpu_ttm_tt_set_userptr - Initialize userptr GTT ttm_tt for the current
* task
*
* @ttm: The ttm_tt object to bind this userptr object to
* @bo: The ttm_buffer_object to bind this userptr to
* @addr: The address in the current tasks VM space to use
* @flags: Requirements of userptr object.
*
* Called by amdgpu_gem_userptr_ioctl() to bind userptr pages
* to current task
*/
int amdgpu_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
uint32_t flags)
int amdgpu_ttm_tt_set_userptr(struct ttm_buffer_object *bo,
uint64_t addr, uint32_t flags)
{
struct amdgpu_ttm_tt *gtt = (void *)ttm;
struct amdgpu_ttm_tt *gtt;
if (gtt == NULL)
return -EINVAL;
if (!bo->ttm) {
/* TODO: We want a separate TTM object type for userptrs */
bo->ttm = amdgpu_ttm_tt_create(bo, 0);
if (bo->ttm == NULL)
return -ENOMEM;
}
gtt = (void*)bo->ttm;
gtt->userptr = addr;
gtt->userflags = flags;
@ -1557,7 +1507,7 @@ bool amdgpu_ttm_tt_is_readonly(struct ttm_tt *ttm)
*
* Figure out the flags to use for a VM PDE (Page Directory Entry).
*/
uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_mem_reg *mem)
uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_resource *mem)
{
uint64_t flags = 0;
@ -1583,7 +1533,7 @@ uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_mem_reg *mem)
* Figure out the flags to use for a VM PTE (Page Table Entry).
*/
uint64_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm,
struct ttm_mem_reg *mem)
struct ttm_resource *mem)
{
uint64_t flags = amdgpu_ttm_tt_pde_flags(ttm, mem);
@ -1741,7 +1691,6 @@ static struct ttm_bo_driver amdgpu_bo_driver = {
.ttm_tt_create = &amdgpu_ttm_tt_create,
.ttm_tt_populate = &amdgpu_ttm_tt_populate,
.ttm_tt_unpopulate = &amdgpu_ttm_tt_unpopulate,
.init_mem_type = &amdgpu_init_mem_type,
.eviction_valuable = amdgpu_ttm_bo_eviction_valuable,
.evict_flags = &amdgpu_evict_flags,
.move = &amdgpu_bo_move,
@ -1936,8 +1885,7 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
adev->mman.bdev.no_retry = true;
/* Initialize VRAM pool with all of VRAM divided into pages */
r = ttm_bo_init_mm(&adev->mman.bdev, TTM_PL_VRAM,
adev->gmc.real_vram_size >> PAGE_SHIFT);
r = amdgpu_vram_mgr_init(adev);
if (r) {
DRM_ERROR("Failed initializing VRAM heap.\n");
return r;
@ -2004,7 +1952,7 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
gtt_size = (uint64_t)amdgpu_gtt_size << 20;
/* Initialize GTT memory pool */
r = ttm_bo_init_mm(&adev->mman.bdev, TTM_PL_TT, gtt_size >> PAGE_SHIFT);
r = amdgpu_gtt_mgr_init(adev, gtt_size);
if (r) {
DRM_ERROR("Failed initializing GTT heap.\n");
return r;
@ -2013,22 +1961,19 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
(unsigned)(gtt_size / (1024 * 1024)));
/* Initialize various on-chip memory pools */
r = ttm_bo_init_mm(&adev->mman.bdev, AMDGPU_PL_GDS,
adev->gds.gds_size);
r = amdgpu_ttm_init_on_chip(adev, AMDGPU_PL_GDS, adev->gds.gds_size);
if (r) {
DRM_ERROR("Failed initializing GDS heap.\n");
return r;
}
r = ttm_bo_init_mm(&adev->mman.bdev, AMDGPU_PL_GWS,
adev->gds.gws_size);
r = amdgpu_ttm_init_on_chip(adev, AMDGPU_PL_GWS, adev->gds.gws_size);
if (r) {
DRM_ERROR("Failed initializing gws heap.\n");
return r;
}
r = ttm_bo_init_mm(&adev->mman.bdev, AMDGPU_PL_OA,
adev->gds.oa_size);
r = amdgpu_ttm_init_on_chip(adev, AMDGPU_PL_OA, adev->gds.oa_size);
if (r) {
DRM_ERROR("Failed initializing oa heap.\n");
return r;
@ -2064,11 +2009,11 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev)
iounmap(adev->mman.aper_base_kaddr);
adev->mman.aper_base_kaddr = NULL;
ttm_bo_clean_mm(&adev->mman.bdev, TTM_PL_VRAM);
ttm_bo_clean_mm(&adev->mman.bdev, TTM_PL_TT);
ttm_bo_clean_mm(&adev->mman.bdev, AMDGPU_PL_GDS);
ttm_bo_clean_mm(&adev->mman.bdev, AMDGPU_PL_GWS);
ttm_bo_clean_mm(&adev->mman.bdev, AMDGPU_PL_OA);
amdgpu_vram_mgr_fini(adev);
amdgpu_gtt_mgr_fini(adev);
ttm_range_man_fini(&adev->mman.bdev, AMDGPU_PL_GDS);
ttm_range_man_fini(&adev->mman.bdev, AMDGPU_PL_GWS);
ttm_range_man_fini(&adev->mman.bdev, AMDGPU_PL_OA);
ttm_bo_device_release(&adev->mman.bdev);
adev->mman.initialized = false;
DRM_INFO("amdgpu: ttm finalized\n");
@ -2085,7 +2030,7 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev)
*/
void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable)
{
struct ttm_mem_type_manager *man = &adev->mman.bdev.man[TTM_PL_VRAM];
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
uint64_t size;
int r;
@ -2307,7 +2252,7 @@ static int amdgpu_mm_dump_table(struct seq_file *m, void *data)
unsigned ttm_pl = (uintptr_t)node->info_ent->data;
struct drm_device *dev = node->minor->dev;
struct amdgpu_device *adev = dev->dev_private;
struct ttm_mem_type_manager *man = &adev->mman.bdev.man[ttm_pl];
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, ttm_pl);
struct drm_printer p = drm_seq_file_printer(m);
man->func->debug(man, &p);

View file

@ -41,6 +41,21 @@
#define AMDGPU_POISON 0xd0bed0be
struct amdgpu_vram_mgr {
struct ttm_resource_manager manager;
struct drm_mm mm;
spinlock_t lock;
atomic64_t usage;
atomic64_t vis_usage;
};
struct amdgpu_gtt_mgr {
struct ttm_resource_manager manager;
struct drm_mm mm;
spinlock_t lock;
atomic64_t available;
};
struct amdgpu_mman {
struct ttm_bo_device bdev;
bool mem_global_referenced;
@ -59,24 +74,29 @@ struct amdgpu_mman {
struct mutex gtt_window_lock;
/* Scheduler entity for buffer moves */
struct drm_sched_entity entity;
struct amdgpu_vram_mgr vram_mgr;
struct amdgpu_gtt_mgr gtt_mgr;
};
struct amdgpu_copy_mem {
struct ttm_buffer_object *bo;
struct ttm_mem_reg *mem;
struct ttm_resource *mem;
unsigned long offset;
};
extern const struct ttm_mem_type_manager_func amdgpu_gtt_mgr_func;
extern const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func;
int amdgpu_gtt_mgr_init(struct amdgpu_device *adev, uint64_t gtt_size);
void amdgpu_gtt_mgr_fini(struct amdgpu_device *adev);
int amdgpu_vram_mgr_init(struct amdgpu_device *adev);
void amdgpu_vram_mgr_fini(struct amdgpu_device *adev);
bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_mem_reg *mem);
uint64_t amdgpu_gtt_mgr_usage(struct ttm_mem_type_manager *man);
int amdgpu_gtt_mgr_recover(struct ttm_mem_type_manager *man);
bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_resource *mem);
uint64_t amdgpu_gtt_mgr_usage(struct ttm_resource_manager *man);
int amdgpu_gtt_mgr_recover(struct ttm_resource_manager *man);
u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo);
int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev,
struct ttm_mem_reg *mem,
struct ttm_resource *mem,
struct device *dev,
enum dma_data_direction dir,
struct sg_table **sgt);
@ -84,8 +104,8 @@ void amdgpu_vram_mgr_free_sgt(struct amdgpu_device *adev,
struct device *dev,
enum dma_data_direction dir,
struct sg_table *sgt);
uint64_t amdgpu_vram_mgr_usage(struct ttm_mem_type_manager *man);
uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_mem_type_manager *man);
uint64_t amdgpu_vram_mgr_usage(struct ttm_resource_manager *man);
uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_resource_manager *man);
int amdgpu_ttm_init(struct amdgpu_device *adev);
void amdgpu_ttm_late_init(struct amdgpu_device *adev);
@ -130,8 +150,8 @@ static inline bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm)
#endif
void amdgpu_ttm_tt_set_user_pages(struct ttm_tt *ttm, struct page **pages);
int amdgpu_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
uint32_t flags);
int amdgpu_ttm_tt_set_userptr(struct ttm_buffer_object *bo,
uint64_t addr, uint32_t flags);
bool amdgpu_ttm_tt_has_userptr(struct ttm_tt *ttm);
struct mm_struct *amdgpu_ttm_tt_get_usermm(struct ttm_tt *ttm);
bool amdgpu_ttm_tt_affect_userptr(struct ttm_tt *ttm, unsigned long start,
@ -140,9 +160,9 @@ bool amdgpu_ttm_tt_userptr_invalidated(struct ttm_tt *ttm,
int *last_invalidated);
bool amdgpu_ttm_tt_is_userptr(struct ttm_tt *ttm);
bool amdgpu_ttm_tt_is_readonly(struct ttm_tt *ttm);
uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_mem_reg *mem);
uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_resource *mem);
uint64_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm,
struct ttm_mem_reg *mem);
struct ttm_resource *mem);
int amdgpu_ttm_debugfs_init(struct amdgpu_device *adev);

View file

@ -1765,7 +1765,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va,
struct amdgpu_vm *vm = bo_va->base.vm;
struct amdgpu_bo_va_mapping *mapping;
dma_addr_t *pages_addr = NULL;
struct ttm_mem_reg *mem;
struct ttm_resource *mem;
struct drm_mm_node *nodes;
struct dma_fence **last_update;
struct dma_resv *resv;

View file

@ -28,12 +28,15 @@
#include "amdgpu_atomfirmware.h"
#include "atom.h"
struct amdgpu_vram_mgr {
struct drm_mm mm;
spinlock_t lock;
atomic64_t usage;
atomic64_t vis_usage;
};
static inline struct amdgpu_vram_mgr *to_vram_mgr(struct ttm_resource_manager *man)
{
return container_of(man, struct amdgpu_vram_mgr, manager);
}
static inline struct amdgpu_device *to_amdgpu_device(struct amdgpu_vram_mgr *mgr)
{
return container_of(mgr, struct amdgpu_device, mman.vram_mgr);
}
/**
* DOC: mem_info_vram_total
@ -82,9 +85,9 @@ static ssize_t amdgpu_mem_info_vram_used_show(struct device *dev,
{
struct drm_device *ddev = dev_get_drvdata(dev);
struct amdgpu_device *adev = ddev->dev_private;
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
return snprintf(buf, PAGE_SIZE, "%llu\n",
amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]));
amdgpu_vram_mgr_usage(man));
}
/**
@ -100,9 +103,9 @@ static ssize_t amdgpu_mem_info_vis_vram_used_show(struct device *dev,
{
struct drm_device *ddev = dev_get_drvdata(dev);
struct amdgpu_device *adev = ddev->dev_private;
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
return snprintf(buf, PAGE_SIZE, "%llu\n",
amdgpu_vram_mgr_vis_usage(&adev->mman.bdev.man[TTM_PL_VRAM]));
amdgpu_vram_mgr_vis_usage(man));
}
static ssize_t amdgpu_mem_info_vram_vendor(struct device *dev,
@ -158,6 +161,8 @@ static const struct attribute *amdgpu_vram_mgr_attributes[] = {
NULL
};
static const struct ttm_resource_manager_func amdgpu_vram_mgr_func;
/**
* amdgpu_vram_mgr_init - init VRAM manager and DRM MM
*
@ -166,26 +171,29 @@ static const struct attribute *amdgpu_vram_mgr_attributes[] = {
*
* Allocate and initialize the VRAM manager.
*/
static int amdgpu_vram_mgr_init(struct ttm_mem_type_manager *man,
unsigned long p_size)
int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
struct amdgpu_vram_mgr *mgr;
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
struct ttm_resource_manager *man = &mgr->manager;
int ret;
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
if (!mgr)
return -ENOMEM;
man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC;
man->default_caching = TTM_PL_FLAG_WC;
drm_mm_init(&mgr->mm, 0, p_size);
ttm_resource_manager_init(man, adev->gmc.real_vram_size >> PAGE_SHIFT);
man->func = &amdgpu_vram_mgr_func;
drm_mm_init(&mgr->mm, 0, man->size);
spin_lock_init(&mgr->lock);
man->priv = mgr;
/* Add the two VRAM-related sysfs files */
ret = sysfs_create_files(&adev->dev->kobj, amdgpu_vram_mgr_attributes);
if (ret)
DRM_ERROR("Failed to register sysfs\n");
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager);
ttm_resource_manager_set_used(man, true);
return 0;
}
@ -197,18 +205,26 @@ static int amdgpu_vram_mgr_init(struct ttm_mem_type_manager *man,
* Destroy and free the VRAM manager, returns -EBUSY if ranges are still
* allocated inside it.
*/
static int amdgpu_vram_mgr_fini(struct ttm_mem_type_manager *man)
void amdgpu_vram_mgr_fini(struct amdgpu_device *adev)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
struct amdgpu_vram_mgr *mgr = man->priv;
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
struct ttm_resource_manager *man = &mgr->manager;
int ret;
ttm_resource_manager_set_used(man, false);
ret = ttm_resource_manager_force_list_clean(&adev->mman.bdev, man);
if (ret)
return;
spin_lock(&mgr->lock);
drm_mm_takedown(&mgr->mm);
spin_unlock(&mgr->lock);
kfree(mgr);
man->priv = NULL;
sysfs_remove_files(&adev->dev->kobj, amdgpu_vram_mgr_attributes);
return 0;
ttm_resource_manager_cleanup(man);
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, NULL);
}
/**
@ -243,7 +259,7 @@ static u64 amdgpu_vram_mgr_vis_size(struct amdgpu_device *adev,
u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
struct ttm_mem_reg *mem = &bo->tbo.mem;
struct ttm_resource *mem = &bo->tbo.mem;
struct drm_mm_node *nodes = mem->mm_node;
unsigned pages = mem->num_pages;
u64 usage;
@ -263,13 +279,13 @@ u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)
/**
* amdgpu_vram_mgr_virt_start - update virtual start address
*
* @mem: ttm_mem_reg to update
* @mem: ttm_resource to update
* @node: just allocated node
*
* Calculate a virtual BO start address to easily check if everything is CPU
* accessible.
*/
static void amdgpu_vram_mgr_virt_start(struct ttm_mem_reg *mem,
static void amdgpu_vram_mgr_virt_start(struct ttm_resource *mem,
struct drm_mm_node *node)
{
unsigned long start;
@ -292,13 +308,13 @@ static void amdgpu_vram_mgr_virt_start(struct ttm_mem_reg *mem,
*
* Allocate VRAM for the given BO.
*/
static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man,
static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
struct ttm_buffer_object *tbo,
const struct ttm_place *place,
struct ttm_mem_reg *mem)
struct ttm_resource *mem)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
struct amdgpu_vram_mgr *mgr = man->priv;
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_device *adev = to_amdgpu_device(mgr);
struct drm_mm *mm = &mgr->mm;
struct drm_mm_node *nodes;
enum drm_mm_insert_mode mode;
@ -410,11 +426,11 @@ static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man,
*
* Free the allocated VRAM again.
*/
static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man,
struct ttm_mem_reg *mem)
static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man,
struct ttm_resource *mem)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
struct amdgpu_vram_mgr *mgr = man->priv;
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_device *adev = to_amdgpu_device(mgr);
struct drm_mm_node *nodes = mem->mm_node;
uint64_t usage = 0, vis_usage = 0;
unsigned pages = mem->num_pages;
@ -451,7 +467,7 @@ static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man,
* Allocate and fill a sg table from a VRAM allocation.
*/
int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev,
struct ttm_mem_reg *mem,
struct ttm_resource *mem,
struct device *dev,
enum dma_data_direction dir,
struct sg_table **sgt)
@ -544,9 +560,9 @@ void amdgpu_vram_mgr_free_sgt(struct amdgpu_device *adev,
*
* Returns how many bytes are used in this domain.
*/
uint64_t amdgpu_vram_mgr_usage(struct ttm_mem_type_manager *man)
uint64_t amdgpu_vram_mgr_usage(struct ttm_resource_manager *man)
{
struct amdgpu_vram_mgr *mgr = man->priv;
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
return atomic64_read(&mgr->usage);
}
@ -558,9 +574,9 @@ uint64_t amdgpu_vram_mgr_usage(struct ttm_mem_type_manager *man)
*
* Returns how many bytes are used in the visible part of VRAM
*/
uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_mem_type_manager *man)
uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_resource_manager *man)
{
struct amdgpu_vram_mgr *mgr = man->priv;
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
return atomic64_read(&mgr->vis_usage);
}
@ -573,10 +589,10 @@ uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_mem_type_manager *man)
*
* Dump the table content using printk.
*/
static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man,
static void amdgpu_vram_mgr_debug(struct ttm_resource_manager *man,
struct drm_printer *printer)
{
struct amdgpu_vram_mgr *mgr = man->priv;
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
spin_lock(&mgr->lock);
drm_mm_print(&mgr->mm, printer);
@ -587,10 +603,8 @@ static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man,
amdgpu_vram_mgr_vis_usage(man) >> 20);
}
const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = {
.init = amdgpu_vram_mgr_init,
.takedown = amdgpu_vram_mgr_fini,
.get_node = amdgpu_vram_mgr_new,
.put_node = amdgpu_vram_mgr_del,
.debug = amdgpu_vram_mgr_debug
static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = {
.alloc = amdgpu_vram_mgr_new,
.free = amdgpu_vram_mgr_del,
.debug = amdgpu_vram_mgr_debug
};

View file

@ -328,6 +328,22 @@ static void amdgpu_atombios_dp_probe_oui(struct amdgpu_connector *amdgpu_connect
buf[0], buf[1], buf[2]);
}
static void amdgpu_atombios_dp_ds_ports(struct amdgpu_connector *amdgpu_connector)
{
struct amdgpu_connector_atom_dig *dig_connector = amdgpu_connector->con_priv;
int ret;
if (dig_connector->dpcd[DP_DPCD_REV] > 0x10) {
ret = drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux,
DP_DOWNSTREAM_PORT_0,
dig_connector->downstream_ports,
DP_MAX_DOWNSTREAM_PORTS);
if (ret)
memset(dig_connector->downstream_ports, 0,
DP_MAX_DOWNSTREAM_PORTS);
}
}
int amdgpu_atombios_dp_get_dpcd(struct amdgpu_connector *amdgpu_connector)
{
struct amdgpu_connector_atom_dig *dig_connector = amdgpu_connector->con_priv;
@ -343,7 +359,7 @@ int amdgpu_atombios_dp_get_dpcd(struct amdgpu_connector *amdgpu_connector)
dig_connector->dpcd);
amdgpu_atombios_dp_probe_oui(amdgpu_connector);
amdgpu_atombios_dp_ds_ports(amdgpu_connector);
return 0;
}

View file

@ -127,6 +127,42 @@ MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU);
static int amdgpu_dm_init(struct amdgpu_device *adev);
static void amdgpu_dm_fini(struct amdgpu_device *adev);
static enum drm_mode_subconnector get_subconnector_type(struct dc_link *link)
{
switch (link->dpcd_caps.dongle_type) {
case DISPLAY_DONGLE_NONE:
return DRM_MODE_SUBCONNECTOR_Native;
case DISPLAY_DONGLE_DP_VGA_CONVERTER:
return DRM_MODE_SUBCONNECTOR_VGA;
case DISPLAY_DONGLE_DP_DVI_CONVERTER:
case DISPLAY_DONGLE_DP_DVI_DONGLE:
return DRM_MODE_SUBCONNECTOR_DVID;
case DISPLAY_DONGLE_DP_HDMI_CONVERTER:
case DISPLAY_DONGLE_DP_HDMI_DONGLE:
return DRM_MODE_SUBCONNECTOR_HDMIA;
case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE:
default:
return DRM_MODE_SUBCONNECTOR_Unknown;
}
}
static void update_subconnector_property(struct amdgpu_dm_connector *aconnector)
{
struct dc_link *link = aconnector->dc_link;
struct drm_connector *connector = &aconnector->base;
enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
return;
if (aconnector->dc_sink)
subconnector = get_subconnector_type(link);
drm_object_property_set_value(&connector->base,
connector->dev->mode_config.dp_subconnector_property,
subconnector);
}
/*
* initializes drm_device display related structures, based on the information
* provided by DAL. The drm strcutures are: drm_crtc, drm_connector,
@ -2095,7 +2131,6 @@ void amdgpu_dm_update_connector_after_detect(
if (aconnector->mst_mgr.mst_state == true)
return;
sink = aconnector->dc_link->local_sink;
if (sink)
dc_sink_retain(sink);
@ -2222,6 +2257,8 @@ void amdgpu_dm_update_connector_after_detect(
mutex_unlock(&dev->mode_config.mutex);
update_subconnector_property(aconnector);
if (sink)
dc_sink_release(sink);
}
@ -4757,6 +4794,8 @@ amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
else
connected = (aconnector->base.force == DRM_FORCE_ON);
update_subconnector_property(aconnector);
return (connected ? connector_status_connected :
connector_status_disconnected);
}

View file

@ -26,6 +26,7 @@
#include <linux/version.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_dp_mst_helper.h>
#include <drm/drm_dp_helper.h>
#include "dm_services.h"
#include "amdgpu.h"
#include "amdgpu_dm.h"
@ -431,6 +432,8 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
16,
4,
aconnector->connector_id);
drm_connector_attach_dp_subconnector_property(&aconnector->base);
}
int dm_mst_get_pbn_divider(struct dc_link *link)

View file

@ -346,7 +346,7 @@ static bool malidp_check_pages_threshold(struct malidp_plane_state *ms,
if (cma_obj->sgt)
sgt = cma_obj->sgt;
else
sgt = obj->dev->driver->gem_prime_get_sg_table(obj);
sgt = obj->funcs->get_sg_table(obj);
if (!sgt)
return false;

View file

@ -47,7 +47,7 @@ static void ast_cursor_fini(struct ast_private *ast)
static void ast_cursor_release(struct drm_device *dev, void *ptr)
{
struct ast_private *ast = dev->dev_private;
struct ast_private *ast = to_ast_private(dev);
ast_cursor_fini(ast);
}
@ -57,7 +57,7 @@ static void ast_cursor_release(struct drm_device *dev, void *ptr)
*/
int ast_cursor_init(struct ast_private *ast)
{
struct drm_device *dev = ast->dev;
struct drm_device *dev = &ast->base;
size_t size, i;
struct drm_gem_vram_object *gbo;
void __iomem *vaddr;
@ -168,7 +168,7 @@ static void update_cursor_image(u8 __iomem *dst, const u8 *src, int width, int h
int ast_cursor_blit(struct ast_private *ast, struct drm_framebuffer *fb)
{
struct drm_device *dev = ast->dev;
struct drm_device *dev = &ast->base;
struct drm_gem_vram_object *gbo;
int ret;
void *src;
@ -217,7 +217,7 @@ static void ast_cursor_set_base(struct ast_private *ast, u64 address)
void ast_cursor_page_flip(struct ast_private *ast)
{
struct drm_device *dev = ast->dev;
struct drm_device *dev = &ast->base;
struct drm_gem_vram_object *gbo;
s64 off;
@ -253,7 +253,8 @@ void ast_cursor_show(struct ast_private *ast, int x, int y,
unsigned int offset_x, unsigned int offset_y)
{
u8 x_offset, y_offset;
u8 __iomem *dst, __iomem *sig;
u8 __iomem *dst;
u8 __iomem *sig;
u8 jreg;
dst = ast->cursor.vaddr[ast->cursor.next_index];

View file

@ -8,11 +8,24 @@
MODULE_FIRMWARE("ast_dp501_fw.bin");
static void ast_release_firmware(void *data)
{
struct ast_private *ast = data;
release_firmware(ast->dp501_fw);
ast->dp501_fw = NULL;
}
static int ast_load_dp501_microcode(struct drm_device *dev)
{
struct ast_private *ast = to_ast_private(dev);
int ret;
return request_firmware(&ast->dp501_fw, "ast_dp501_fw.bin", dev->dev);
ret = request_firmware(&ast->dp501_fw, "ast_dp501_fw.bin", dev->dev);
if (ret)
return ret;
return devm_add_action_or_reset(dev->dev, ast_release_firmware, ast);
}
static void send_ack(struct ast_private *ast)
@ -435,11 +448,3 @@ void ast_init_3rdtx(struct drm_device *dev)
}
}
}
void ast_release_firmware(struct drm_device *dev)
{
struct ast_private *ast = to_ast_private(dev);
release_firmware(ast->dp501_fw);
ast->dp501_fw = NULL;
}

View file

@ -43,9 +43,33 @@ int ast_modeset = -1;
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
module_param_named(modeset, ast_modeset, int, 0400);
#define PCI_VENDOR_ASPEED 0x1a03
/*
* DRM driver
*/
static struct drm_driver driver;
DEFINE_DRM_GEM_FOPS(ast_fops);
static struct drm_driver ast_driver = {
.driver_features = DRIVER_ATOMIC |
DRIVER_GEM |
DRIVER_MODESET,
.fops = &ast_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
DRM_GEM_VRAM_DRIVER
};
/*
* PCI driver
*/
#define PCI_VENDOR_ASPEED 0x1a03
#define AST_VGA_DEVICE(id, info) { \
.class = PCI_BASE_CLASS_DISPLAY << 16, \
@ -56,13 +80,13 @@ static struct drm_driver driver;
.subdevice = PCI_ANY_ID, \
.driver_data = (unsigned long) info }
static const struct pci_device_id pciidlist[] = {
static const struct pci_device_id ast_pciidlist[] = {
AST_VGA_DEVICE(PCI_CHIP_AST2000, NULL),
AST_VGA_DEVICE(PCI_CHIP_AST2100, NULL),
{0, 0, 0},
};
MODULE_DEVICE_TABLE(pci, pciidlist);
MODULE_DEVICE_TABLE(pci, ast_pciidlist);
static void ast_kick_out_firmware_fb(struct pci_dev *pdev)
{
@ -85,6 +109,7 @@ static void ast_kick_out_firmware_fb(struct pci_dev *pdev)
static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct ast_private *ast;
struct drm_device *dev;
int ret;
@ -94,41 +119,25 @@ static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
return ret;
dev = drm_dev_alloc(&driver, &pdev->dev);
if (IS_ERR(dev))
return PTR_ERR(dev);
dev->pdev = pdev;
pci_set_drvdata(pdev, dev);
ret = ast_driver_load(dev, ent->driver_data);
if (ret)
goto err_drm_dev_put;
ast = ast_device_create(&ast_driver, pdev, ent->driver_data);
if (IS_ERR(ast))
return PTR_ERR(ast);
dev = &ast->base;
ret = drm_dev_register(dev, ent->driver_data);
if (ret)
goto err_ast_driver_unload;
return ret;
drm_fbdev_generic_setup(dev, 32);
return 0;
err_ast_driver_unload:
ast_driver_unload(dev);
err_drm_dev_put:
drm_dev_put(dev);
return ret;
}
static void
ast_pci_remove(struct pci_dev *pdev)
static void ast_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_dev_unregister(dev);
ast_driver_unload(dev);
drm_dev_put(dev);
}
static int ast_drm_freeze(struct drm_device *dev)
@ -217,30 +226,12 @@ static const struct dev_pm_ops ast_pm_ops = {
static struct pci_driver ast_pci_driver = {
.name = DRIVER_NAME,
.id_table = pciidlist,
.id_table = ast_pciidlist,
.probe = ast_pci_probe,
.remove = ast_pci_remove,
.driver.pm = &ast_pm_ops,
};
DEFINE_DRM_GEM_FOPS(ast_fops);
static struct drm_driver driver = {
.driver_features = DRIVER_ATOMIC |
DRIVER_GEM |
DRIVER_MODESET,
.fops = &ast_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
DRM_GEM_VRAM_DRIVER
};
static int __init ast_init(void)
{
if (vgacon_text_force() && ast_modeset == -1)
@ -261,4 +252,3 @@ module_exit(ast_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL and additional rights");

View file

@ -98,9 +98,25 @@ enum ast_tx_chip {
#define AST_HWC_SIGNATURE_HOTSPOTX 0x14
#define AST_HWC_SIGNATURE_HOTSPOTY 0x18
struct ast_i2c_chan {
struct i2c_adapter adapter;
struct drm_device *dev;
struct i2c_algo_bit_data bit;
};
struct ast_connector {
struct drm_connector base;
struct ast_i2c_chan *i2c;
};
static inline struct ast_connector *
to_ast_connector(struct drm_connector *connector)
{
return container_of(connector, struct ast_connector, base);
}
struct ast_private {
struct drm_device *dev;
struct drm_device base;
void __iomem *regs;
void __iomem *ioregs;
@ -119,9 +135,11 @@ struct ast_private {
unsigned int next_index;
} cursor;
struct drm_encoder encoder;
struct drm_plane primary_plane;
struct drm_plane cursor_plane;
struct drm_crtc crtc;
struct drm_encoder encoder;
struct ast_connector connector;
bool support_wide_screen;
enum {
@ -138,11 +156,12 @@ struct ast_private {
static inline struct ast_private *to_ast_private(struct drm_device *dev)
{
return dev->dev_private;
return container_of(dev, struct ast_private, base);
}
int ast_driver_load(struct drm_device *dev, unsigned long flags);
void ast_driver_unload(struct drm_device *dev);
struct ast_private *ast_device_create(struct drm_driver *drv,
struct pci_dev *pdev,
unsigned long flags);
#define AST_IO_AR_PORT_WRITE (0x40)
#define AST_IO_MISC_PORT_WRITE (0x42)
@ -226,19 +245,6 @@ static inline void ast_open_key(struct ast_private *ast)
#define AST_VIDMEM_DEFAULT_SIZE AST_VIDMEM_SIZE_8M
struct ast_i2c_chan {
struct i2c_adapter adapter;
struct drm_device *dev;
struct i2c_algo_bit_data bit;
};
struct ast_connector {
struct drm_connector base;
struct ast_i2c_chan *i2c;
};
#define to_ast_connector(x) container_of(x, struct ast_connector, base)
struct ast_vbios_stdtable {
u8 misc;
u8 seq[4];
@ -305,7 +311,6 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size);
bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata);
u8 ast_get_dp501_max_clk(struct drm_device *dev);
void ast_init_3rdtx(struct drm_device *dev);
void ast_release_firmware(struct drm_device *dev);
/* ast_cursor.c */
int ast_cursor_init(struct ast_private *ast);

View file

@ -30,8 +30,10 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_vram_helper.h>
#include <drm/drm_managed.h>
#include "ast_drv.h"
@ -230,11 +232,11 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
ast->tx_chip_type = AST_TX_SIL164;
break;
case 0x08:
ast->dp501_fw_addr = kzalloc(32*1024, GFP_KERNEL);
ast->dp501_fw_addr = drmm_kzalloc(dev, 32*1024, GFP_KERNEL);
if (ast->dp501_fw_addr) {
/* backup firmware */
if (ast_backup_fw(dev, ast->dp501_fw_addr, 32*1024)) {
kfree(ast->dp501_fw_addr);
drmm_kfree(dev, ast->dp501_fw_addr);
ast->dp501_fw_addr = NULL;
}
}
@ -378,24 +380,38 @@ static int ast_get_dram_info(struct drm_device *dev)
return 0;
}
int ast_driver_load(struct drm_device *dev, unsigned long flags)
/*
* Run this function as part of the HW device cleanup; not
* when the DRM device gets released.
*/
static void ast_device_release(void *data)
{
struct ast_private *ast = data;
/* enable standard VGA decode */
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x04);
}
struct ast_private *ast_device_create(struct drm_driver *drv,
struct pci_dev *pdev,
unsigned long flags)
{
struct drm_device *dev;
struct ast_private *ast;
bool need_post;
int ret = 0;
ast = kzalloc(sizeof(struct ast_private), GFP_KERNEL);
if (!ast)
return -ENOMEM;
ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_private, base);
if (IS_ERR(ast))
return ast;
dev = &ast->base;
dev->dev_private = ast;
ast->dev = dev;
dev->pdev = pdev;
pci_set_drvdata(pdev, dev);
ast->regs = pci_iomap(dev->pdev, 1, 0);
if (!ast->regs) {
ret = -EIO;
goto out_free;
}
if (!ast->regs)
return ERR_PTR(-EIO);
/*
* If we don't have IO space at all, use MMIO now and
@ -410,17 +426,16 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
/* "map" IO regs if the above hasn't done so already */
if (!ast->ioregs) {
ast->ioregs = pci_iomap(dev->pdev, 2, 0);
if (!ast->ioregs) {
ret = -EIO;
goto out_free;
}
if (!ast->ioregs)
return ERR_PTR(-EIO);
}
ast_detect_chip(dev, &need_post);
ret = ast_get_dram_info(dev);
if (ret)
goto out_free;
return ERR_PTR(ret);
drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d\n",
ast->mclk, ast->dram_type, ast->dram_bus_width);
@ -429,28 +444,15 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
ret = ast_mm_init(ast);
if (ret)
goto out_free;
return ERR_PTR(ret);
ret = ast_mode_config_init(ast);
if (ret)
goto out_free;
return ERR_PTR(ret);
return 0;
out_free:
kfree(ast);
dev->dev_private = NULL;
return ret;
}
void ast_driver_unload(struct drm_device *dev)
{
struct ast_private *ast = to_ast_private(dev);
/* enable standard VGA decode */
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x04);
ast_release_firmware(dev);
kfree(ast->dp501_fw_addr);
kfree(ast);
ret = devm_add_action_or_reset(dev->dev, ast_device_release, ast);
if (ret)
return ERR_PTR(ret);
return ast;
}

View file

@ -85,9 +85,9 @@ static void ast_mm_release(struct drm_device *dev, void *ptr)
int ast_mm_init(struct ast_private *ast)
{
struct drm_device *dev = &ast->base;
u32 vram_size;
int ret;
struct drm_device *dev = ast->dev;
vram_size = ast_get_vram_size(ast);

View file

@ -663,7 +663,7 @@ ast_cursor_plane_helper_atomic_update(struct drm_plane *plane,
{
struct drm_plane_state *state = plane->state;
struct drm_framebuffer *fb = state->fb;
struct ast_private *ast = plane->dev->dev_private;
struct ast_private *ast = to_ast_private(plane->dev);
unsigned int offset_x, offset_y;
offset_x = AST_MAX_HWC_WIDTH - fb->width;
@ -831,12 +831,6 @@ static void ast_crtc_reset(struct drm_crtc *crtc)
__drm_atomic_helper_crtc_reset(crtc, &ast_state->base);
}
static void ast_crtc_destroy(struct drm_crtc *crtc)
{
drm_crtc_cleanup(crtc);
kfree(crtc);
}
static struct drm_crtc_state *
ast_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
{
@ -872,7 +866,7 @@ static void ast_crtc_atomic_destroy_state(struct drm_crtc *crtc,
static const struct drm_crtc_funcs ast_crtc_funcs = {
.reset = ast_crtc_reset,
.gamma_set = drm_atomic_helper_legacy_gamma_set,
.destroy = ast_crtc_destroy,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = ast_crtc_atomic_duplicate_state,
@ -882,27 +876,19 @@ static const struct drm_crtc_funcs ast_crtc_funcs = {
static int ast_crtc_init(struct drm_device *dev)
{
struct ast_private *ast = to_ast_private(dev);
struct drm_crtc *crtc;
struct drm_crtc *crtc = &ast->crtc;
int ret;
crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
if (!crtc)
return -ENOMEM;
ret = drm_crtc_init_with_planes(dev, crtc, &ast->primary_plane,
&ast->cursor_plane, &ast_crtc_funcs,
NULL);
if (ret)
goto err_kfree;
return ret;
drm_mode_crtc_set_gamma_size(crtc, 256);
drm_crtc_helper_add(crtc, &ast_crtc_helper_funcs);
return 0;
err_kfree:
kfree(crtc);
return ret;
}
/*
@ -1021,7 +1007,6 @@ static void ast_connector_destroy(struct drm_connector *connector)
struct ast_connector *ast_connector = to_ast_connector(connector);
ast_i2c_destroy(ast_connector->i2c);
drm_connector_cleanup(connector);
kfree(connector);
}
static const struct drm_connector_helper_funcs ast_connector_helper_funcs = {
@ -1039,15 +1024,11 @@ static const struct drm_connector_funcs ast_connector_funcs = {
static int ast_connector_init(struct drm_device *dev)
{
struct ast_connector *ast_connector;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct ast_private *ast = to_ast_private(dev);
struct ast_connector *ast_connector = &ast->connector;
struct drm_connector *connector = &ast_connector->base;
struct drm_encoder *encoder = &ast->encoder;
ast_connector = kzalloc(sizeof(struct ast_connector), GFP_KERNEL);
if (!ast_connector)
return -ENOMEM;
connector = &ast_connector->base;
ast_connector->i2c = ast_i2c_create(dev);
if (!ast_connector->i2c)
drm_err(dev, "failed to add ddc bus for connector\n");
@ -1064,7 +1045,6 @@ static int ast_connector_init(struct drm_device *dev)
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
encoder = list_first_entry(&dev->mode_config.encoder_list, struct drm_encoder, head);
drm_connector_attach_encoder(connector, encoder);
return 0;
@ -1083,7 +1063,7 @@ static const struct drm_mode_config_funcs ast_mode_config_funcs = {
int ast_mode_config_init(struct ast_private *ast)
{
struct drm_device *dev = ast->dev;
struct drm_device *dev = &ast->base;
int ret;
ret = ast_cursor_init(ast);
@ -1099,7 +1079,7 @@ int ast_mode_config_init(struct ast_private *ast)
dev->mode_config.min_height = 0;
dev->mode_config.preferred_depth = 24;
dev->mode_config.prefer_shadow = 1;
dev->mode_config.fb_base = pci_resource_start(ast->dev->pdev, 0);
dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0);
if (ast->chip == AST2100 ||
ast->chip == AST2200 ||

View file

@ -365,12 +365,12 @@ static void ast_init_dram_reg(struct drm_device *dev)
void ast_post_gpu(struct drm_device *dev)
{
u32 reg;
struct ast_private *ast = to_ast_private(dev);
u32 reg;
pci_read_config_dword(ast->dev->pdev, 0x04, &reg);
pci_read_config_dword(dev->pdev, 0x04, &reg);
reg |= 0x3;
pci_write_config_dword(ast->dev->pdev, 0x04, reg);
pci_write_config_dword(dev->pdev, 0x04, reg);
ast_enable_vga(dev);
ast_open_key(ast);

View file

@ -48,6 +48,19 @@ config DRM_DISPLAY_CONNECTOR
on ARM-based platforms. Saying Y here when this driver is not needed
will not cause any issue.
config DRM_LONTIUM_LT9611
tristate "Lontium LT9611 DSI/HDMI bridge"
select SND_SOC_HDMI_CODEC if SND_SOC
depends on OF
select DRM_PANEL_BRIDGE
select DRM_KMS_HELPER
select REGMAP_I2C
help
Driver for Lontium LT9611 DSI to HDMI bridge
chip driver that converts dual DSI and I2S to
HDMI signals
Please say Y if you have such hardware.
config DRM_LVDS_CODEC
tristate "Transparent LVDS encoders and decoders support"
depends on OF
@ -153,6 +166,14 @@ config DRM_THINE_THC63LVD1024
help
Thine THC63LVD1024 LVDS/parallel converter driver.
config DRM_TOSHIBA_TC358762
tristate "TC358762 DSI/DPI bridge"
depends on OF
select DRM_MIPI_DSI
select DRM_PANEL_BRIDGE
help
Toshiba TC358762 DSI/DPI bridge driver.
config DRM_TOSHIBA_TC358764
tristate "TC358764 DSI/LVDS bridge"
depends on OF
@ -181,6 +202,16 @@ config DRM_TOSHIBA_TC358768
help
Toshiba TC358768AXBG/TC358778XBG DSI bridge chip driver.
config DRM_TOSHIBA_TC358775
tristate "Toshiba TC358775 DSI/LVDS bridge"
depends on OF
select DRM_KMS_HELPER
select REGMAP_I2C
select DRM_PANEL
select DRM_MIPI_DSI
help
Toshiba TC358775 DSI/LVDS bridge chip driver.
config DRM_TI_TFP410
tristate "TI TFP410 DVI/HDMI bridge"
depends on OF

View file

@ -2,6 +2,7 @@
obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o
obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
@ -12,9 +13,11 @@ obj-$(CONFIG_DRM_SII902X) += sii902x.o
obj-$(CONFIG_DRM_SII9234) += sii9234.o
obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o
obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o
obj-$(CONFIG_DRM_TOSHIBA_TC358762) += tc358762.o
obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o
obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
obj-$(CONFIG_DRM_TOSHIBA_TC358768) += tc358768.o
obj-$(CONFIG_DRM_TOSHIBA_TC358775) += tc358775.o
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o

View file

@ -507,10 +507,6 @@ static const struct drm_connector_helper_funcs anx6345_connector_helper_funcs =
static void
anx6345_connector_destroy(struct drm_connector *connector)
{
struct anx6345 *anx6345 = connector_to_anx6345(connector);
if (anx6345->panel)
drm_panel_detach(anx6345->panel);
drm_connector_cleanup(connector);
}
@ -575,14 +571,6 @@ static int anx6345_bridge_attach(struct drm_bridge *bridge,
return err;
}
if (anx6345->panel) {
err = drm_panel_attach(anx6345->panel, &anx6345->connector);
if (err) {
DRM_ERROR("Failed to attach panel: %d\n", err);
return err;
}
}
return 0;
}

View file

@ -1265,14 +1265,6 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
}
}
if (dp->plat_data->panel) {
ret = drm_panel_attach(dp->plat_data->panel, &dp->connector);
if (ret) {
DRM_ERROR("Failed to attach panel\n");
return ret;
}
}
return 0;
}
@ -1803,7 +1795,6 @@ void analogix_dp_unbind(struct analogix_dp_device *dp)
if (dp->plat_data->panel) {
if (drm_panel_unprepare(dp->plat_data->panel))
DRM_ERROR("failed to turnoff the panel\n");
drm_panel_detach(dp->plat_data->panel);
}
drm_dp_aux_unregister(&dp->aux);

File diff suppressed because it is too large Load diff

View file

@ -61,7 +61,6 @@ struct ge_b850v3_lvds {
struct drm_bridge bridge;
struct i2c_client *stdp4028_i2c;
struct i2c_client *stdp2690_i2c;
struct edid *edid;
};
static struct ge_b850v3_lvds *ge_b850v3_lvds_ptr;
@ -131,22 +130,26 @@ static u8 *stdp2690_get_edid(struct i2c_client *client)
return NULL;
}
static int ge_b850v3_lvds_get_modes(struct drm_connector *connector)
static struct edid *ge_b850v3_lvds_get_edid(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct i2c_client *client;
int num_modes = 0;
client = ge_b850v3_lvds_ptr->stdp2690_i2c;
kfree(ge_b850v3_lvds_ptr->edid);
ge_b850v3_lvds_ptr->edid = (struct edid *)stdp2690_get_edid(client);
return (struct edid *)stdp2690_get_edid(client);
}
if (ge_b850v3_lvds_ptr->edid) {
drm_connector_update_edid_property(connector,
ge_b850v3_lvds_ptr->edid);
num_modes = drm_add_edid_modes(connector,
ge_b850v3_lvds_ptr->edid);
}
static int ge_b850v3_lvds_get_modes(struct drm_connector *connector)
{
struct edid *edid;
int num_modes;
edid = ge_b850v3_lvds_get_edid(&ge_b850v3_lvds_ptr->bridge, connector);
drm_connector_update_edid_property(connector, edid);
num_modes = drm_add_edid_modes(connector, edid);
kfree(edid);
return num_modes;
}
@ -163,8 +166,7 @@ drm_connector_helper_funcs ge_b850v3_lvds_connector_helper_funcs = {
.mode_valid = ge_b850v3_lvds_mode_valid,
};
static enum drm_connector_status ge_b850v3_lvds_detect(
struct drm_connector *connector, bool force)
static enum drm_connector_status ge_b850v3_lvds_bridge_detect(struct drm_bridge *bridge)
{
struct i2c_client *stdp4028_i2c =
ge_b850v3_lvds_ptr->stdp4028_i2c;
@ -182,6 +184,12 @@ static enum drm_connector_status ge_b850v3_lvds_detect(
return connector_status_unknown;
}
static enum drm_connector_status ge_b850v3_lvds_detect(struct drm_connector *connector,
bool force)
{
return ge_b850v3_lvds_bridge_detect(&ge_b850v3_lvds_ptr->bridge);
}
static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = ge_b850v3_lvds_detect,
@ -191,34 +199,11 @@ static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id)
{
struct i2c_client *stdp4028_i2c
= ge_b850v3_lvds_ptr->stdp4028_i2c;
i2c_smbus_write_word_data(stdp4028_i2c,
STDP4028_DPTX_IRQ_STS_REG,
STDP4028_DPTX_IRQ_CLEAR);
if (ge_b850v3_lvds_ptr->connector.dev)
drm_kms_helper_hotplug_event(ge_b850v3_lvds_ptr->connector.dev);
return IRQ_HANDLED;
}
static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
static int ge_b850v3_lvds_create_connector(struct drm_bridge *bridge)
{
struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector;
struct i2c_client *stdp4028_i2c
= ge_b850v3_lvds_ptr->stdp4028_i2c;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;
@ -237,9 +222,29 @@ static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
return ret;
}
ret = drm_connector_attach_encoder(connector, bridge->encoder);
if (ret)
return ret;
return drm_connector_attach_encoder(connector, bridge->encoder);
}
static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id)
{
struct i2c_client *stdp4028_i2c
= ge_b850v3_lvds_ptr->stdp4028_i2c;
i2c_smbus_write_word_data(stdp4028_i2c,
STDP4028_DPTX_IRQ_STS_REG,
STDP4028_DPTX_IRQ_CLEAR);
if (ge_b850v3_lvds_ptr->bridge.dev)
drm_kms_helper_hotplug_event(ge_b850v3_lvds_ptr->bridge.dev);
return IRQ_HANDLED;
}
static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct i2c_client *stdp4028_i2c
= ge_b850v3_lvds_ptr->stdp4028_i2c;
/* Configures the bridge to re-enable interrupts after each ack. */
i2c_smbus_write_word_data(stdp4028_i2c,
@ -251,11 +256,16 @@ static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
STDP4028_DPTX_IRQ_EN_REG,
STDP4028_DPTX_IRQ_CONFIG);
return 0;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
return ge_b850v3_lvds_create_connector(bridge);
}
static const struct drm_bridge_funcs ge_b850v3_lvds_funcs = {
.attach = ge_b850v3_lvds_attach,
.detect = ge_b850v3_lvds_bridge_detect,
.get_edid = ge_b850v3_lvds_get_edid,
};
static int ge_b850v3_lvds_init(struct device *dev)
@ -291,8 +301,6 @@ static void ge_b850v3_lvds_remove(void)
drm_bridge_remove(&ge_b850v3_lvds_ptr->bridge);
kfree(ge_b850v3_lvds_ptr->edid);
ge_b850v3_lvds_ptr = NULL;
out:
mutex_unlock(&ge_b850v3_lvds_dev_mutex);
@ -302,14 +310,21 @@ static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c,
const struct i2c_device_id *id)
{
struct device *dev = &stdp4028_i2c->dev;
int ret;
ge_b850v3_lvds_init(dev);
ret = ge_b850v3_lvds_init(dev);
if (ret)
return ret;
ge_b850v3_lvds_ptr->stdp4028_i2c = stdp4028_i2c;
i2c_set_clientdata(stdp4028_i2c, ge_b850v3_lvds_ptr);
/* drm bridge initialization */
ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs;
ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_EDID;
ge_b850v3_lvds_ptr->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
ge_b850v3_lvds_ptr->bridge.of_node = dev->of_node;
drm_bridge_add(&ge_b850v3_lvds_ptr->bridge);
@ -361,8 +376,12 @@ static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c,
const struct i2c_device_id *id)
{
struct device *dev = &stdp2690_i2c->dev;
int ret;
ge_b850v3_lvds_init(dev);
ret = ge_b850v3_lvds_init(dev);
if (ret)
return ret;
ge_b850v3_lvds_ptr->stdp2690_i2c = stdp2690_i2c;
i2c_set_clientdata(stdp2690_i2c, ge_b850v3_lvds_ptr);

View file

@ -29,8 +29,7 @@ struct ptn3460_bridge {
struct drm_connector connector;
struct i2c_client *client;
struct drm_bridge bridge;
struct edid *edid;
struct drm_panel *panel;
struct drm_bridge *panel_bridge;
struct gpio_desc *gpio_pd_n;
struct gpio_desc *gpio_rst_n;
u32 edid_emulation;
@ -127,11 +126,6 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
usleep_range(10, 20);
gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
if (drm_panel_prepare(ptn_bridge->panel)) {
DRM_ERROR("failed to prepare panel\n");
return;
}
/*
* There's a bug in the PTN chip where it falsely asserts hotplug before
* it is fully functional. We're forced to wait for the maximum start up
@ -146,16 +140,6 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
ptn_bridge->enabled = true;
}
static void ptn3460_enable(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
if (drm_panel_enable(ptn_bridge->panel)) {
DRM_ERROR("failed to enable panel\n");
return;
}
}
static void ptn3460_disable(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
@ -165,36 +149,18 @@ static void ptn3460_disable(struct drm_bridge *bridge)
ptn_bridge->enabled = false;
if (drm_panel_disable(ptn_bridge->panel)) {
DRM_ERROR("failed to disable panel\n");
return;
}
gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
gpiod_set_value(ptn_bridge->gpio_pd_n, 0);
}
static void ptn3460_post_disable(struct drm_bridge *bridge)
static struct edid *ptn3460_get_edid(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
if (drm_panel_unprepare(ptn_bridge->panel)) {
DRM_ERROR("failed to unprepare panel\n");
return;
}
}
static int ptn3460_get_modes(struct drm_connector *connector)
{
struct ptn3460_bridge *ptn_bridge;
u8 *edid;
int ret, num_modes = 0;
bool power_off;
ptn_bridge = connector_to_ptn3460(connector);
if (ptn_bridge->edid)
return drm_add_edid_modes(connector, ptn_bridge->edid);
u8 *edid;
int ret;
power_off = !ptn_bridge->enabled;
ptn3460_pre_enable(&ptn_bridge->bridge);
@ -202,30 +168,40 @@ static int ptn3460_get_modes(struct drm_connector *connector)
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
if (!edid) {
DRM_ERROR("Failed to allocate EDID\n");
return 0;
}
ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
EDID_LENGTH);
if (ret) {
kfree(edid);
goto out;
}
ptn_bridge->edid = (struct edid *)edid;
drm_connector_update_edid_property(connector, ptn_bridge->edid);
num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
EDID_LENGTH);
if (ret) {
kfree(edid);
edid = NULL;
goto out;
}
out:
if (power_off)
ptn3460_disable(&ptn_bridge->bridge);
return (struct edid *)edid;
}
static int ptn3460_connector_get_modes(struct drm_connector *connector)
{
struct ptn3460_bridge *ptn_bridge = connector_to_ptn3460(connector);
struct edid *edid;
int num_modes;
edid = ptn3460_get_edid(&ptn_bridge->bridge, connector);
drm_connector_update_edid_property(connector, edid);
num_modes = drm_add_edid_modes(connector, edid);
kfree(edid);
return num_modes;
}
static const struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
.get_modes = ptn3460_get_modes,
.get_modes = ptn3460_connector_get_modes,
};
static const struct drm_connector_funcs ptn3460_connector_funcs = {
@ -242,10 +218,14 @@ static int ptn3460_bridge_attach(struct drm_bridge *bridge,
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
/* Let this driver create connector if requested */
ret = drm_bridge_attach(bridge->encoder, ptn_bridge->panel_bridge,
bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret < 0)
return ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
@ -265,9 +245,6 @@ static int ptn3460_bridge_attach(struct drm_bridge *bridge,
drm_connector_attach_encoder(&ptn_bridge->connector,
bridge->encoder);
if (ptn_bridge->panel)
drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
drm_helper_hpd_irq_event(ptn_bridge->connector.dev);
return ret;
@ -275,10 +252,9 @@ static int ptn3460_bridge_attach(struct drm_bridge *bridge,
static const struct drm_bridge_funcs ptn3460_bridge_funcs = {
.pre_enable = ptn3460_pre_enable,
.enable = ptn3460_enable,
.disable = ptn3460_disable,
.post_disable = ptn3460_post_disable,
.attach = ptn3460_bridge_attach,
.get_edid = ptn3460_get_edid,
};
static int ptn3460_probe(struct i2c_client *client,
@ -286,6 +262,8 @@ static int ptn3460_probe(struct i2c_client *client,
{
struct device *dev = &client->dev;
struct ptn3460_bridge *ptn_bridge;
struct drm_bridge *panel_bridge;
struct drm_panel *panel;
int ret;
ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL);
@ -293,10 +271,15 @@ static int ptn3460_probe(struct i2c_client *client,
return -ENOMEM;
}
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &ptn_bridge->panel, NULL);
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, NULL);
if (ret)
return ret;
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
if (IS_ERR(panel_bridge))
return PTR_ERR(panel_bridge);
ptn_bridge->panel_bridge = panel_bridge;
ptn_bridge->client = client;
ptn_bridge->gpio_pd_n = devm_gpiod_get(&client->dev, "powerdown",
@ -327,6 +310,8 @@ static int ptn3460_probe(struct i2c_client *client,
}
ptn_bridge->bridge.funcs = &ptn3460_bridge_funcs;
ptn_bridge->bridge.ops = DRM_BRIDGE_OP_EDID;
ptn_bridge->bridge.type = DRM_MODE_CONNECTOR_LVDS;
ptn_bridge->bridge.of_node = dev->of_node;
drm_bridge_add(&ptn_bridge->bridge);

View file

@ -82,18 +82,11 @@ static int panel_bridge_attach(struct drm_bridge *bridge,
drm_connector_attach_encoder(&panel_bridge->connector,
bridge->encoder);
ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector);
if (ret < 0)
return ret;
return 0;
}
static void panel_bridge_detach(struct drm_bridge *bridge)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
drm_panel_detach(panel_bridge->panel);
}
static void panel_bridge_pre_enable(struct drm_bridge *bridge)

View file

@ -42,10 +42,9 @@
#endif
struct ps8622_bridge {
struct drm_connector connector;
struct i2c_client *client;
struct drm_bridge bridge;
struct drm_panel *panel;
struct drm_bridge *panel_bridge;
struct regulator *v12;
struct backlight_device *bl;
@ -64,12 +63,6 @@ static inline struct ps8622_bridge *
return container_of(bridge, struct ps8622_bridge, bridge);
}
static inline struct ps8622_bridge *
connector_to_ps8622(struct drm_connector *connector)
{
return container_of(connector, struct ps8622_bridge, connector);
}
static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val)
{
int ret;
@ -365,11 +358,6 @@ static void ps8622_pre_enable(struct drm_bridge *bridge)
DRM_ERROR("fails to enable ps8622->v12");
}
if (drm_panel_prepare(ps8622->panel)) {
DRM_ERROR("failed to prepare panel\n");
return;
}
gpiod_set_value(ps8622->gpio_slp, 1);
/*
@ -399,24 +387,9 @@ static void ps8622_pre_enable(struct drm_bridge *bridge)
ps8622->enabled = true;
}
static void ps8622_enable(struct drm_bridge *bridge)
{
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
if (drm_panel_enable(ps8622->panel)) {
DRM_ERROR("failed to enable panel\n");
return;
}
}
static void ps8622_disable(struct drm_bridge *bridge)
{
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
if (drm_panel_disable(ps8622->panel)) {
DRM_ERROR("failed to disable panel\n");
return;
}
/* Delay after panel is disabled */
msleep(PS8622_PWMO_END_T12_MS);
}
@ -436,11 +409,6 @@ static void ps8622_post_disable(struct drm_bridge *bridge)
*/
gpiod_set_value(ps8622->gpio_slp, 0);
if (drm_panel_unprepare(ps8622->panel)) {
DRM_ERROR("failed to unprepare panel\n");
return;
}
if (ps8622->v12)
regulator_disable(ps8622->v12);
@ -455,67 +423,17 @@ static void ps8622_post_disable(struct drm_bridge *bridge)
msleep(PS8622_POWER_OFF_T17_MS);
}
static int ps8622_get_modes(struct drm_connector *connector)
{
struct ps8622_bridge *ps8622;
ps8622 = connector_to_ps8622(connector);
return drm_panel_get_modes(ps8622->panel, connector);
}
static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = {
.get_modes = ps8622_get_modes,
};
static const struct drm_connector_funcs ps8622_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int ps8622_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;
}
ps8622->connector.polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(bridge->dev, &ps8622->connector,
&ps8622_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n");
return ret;
}
drm_connector_helper_add(&ps8622->connector,
&ps8622_connector_helper_funcs);
drm_connector_register(&ps8622->connector);
drm_connector_attach_encoder(&ps8622->connector,
bridge->encoder);
if (ps8622->panel)
drm_panel_attach(ps8622->panel, &ps8622->connector);
drm_helper_hpd_irq_event(ps8622->connector.dev);
return ret;
return drm_bridge_attach(ps8622->bridge.encoder, ps8622->panel_bridge,
&ps8622->bridge, flags);
}
static const struct drm_bridge_funcs ps8622_bridge_funcs = {
.pre_enable = ps8622_pre_enable,
.enable = ps8622_enable,
.disable = ps8622_disable,
.post_disable = ps8622_post_disable,
.attach = ps8622_attach,
@ -533,16 +451,23 @@ static int ps8622_probe(struct i2c_client *client,
{
struct device *dev = &client->dev;
struct ps8622_bridge *ps8622;
struct drm_bridge *panel_bridge;
struct drm_panel *panel;
int ret;
ps8622 = devm_kzalloc(dev, sizeof(*ps8622), GFP_KERNEL);
if (!ps8622)
return -ENOMEM;
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &ps8622->panel, NULL);
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, NULL);
if (ret)
return ret;
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
if (IS_ERR(panel_bridge))
return PTR_ERR(panel_bridge);
ps8622->panel_bridge = panel_bridge;
ps8622->client = client;
ps8622->v12 = devm_regulator_get(dev, "vdd12");
@ -595,6 +520,7 @@ static int ps8622_probe(struct i2c_client *client,
}
ps8622->bridge.funcs = &ps8622_bridge_funcs;
ps8622->bridge.type = DRM_MODE_CONNECTOR_LVDS;
ps8622->bridge.of_node = dev->of_node;
drm_bridge_add(&ps8622->bridge);

View file

@ -82,8 +82,11 @@ static int ps8640_bridge_vdo_control(struct ps8640 *ps_bridge,
ret = i2c_smbus_write_i2c_block_data(client, PAGE3_SET_ADD,
sizeof(vdo_ctrl_buf),
vdo_ctrl_buf);
if (ret < 0)
if (ret < 0) {
DRM_ERROR("failed to %sable VDO: %d\n",
ctrl == ENABLE ? "en" : "dis", ret);
return ret;
}
return 0;
}
@ -150,10 +153,8 @@ static void ps8640_pre_enable(struct drm_bridge *bridge)
}
ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE);
if (ret) {
DRM_ERROR("failed to enable VDO: %d\n", ret);
if (ret)
goto err_regulators_disable;
}
/* Switch access edp panel's edid through i2c */
ret = i2c_smbus_write_byte_data(client, PAGE2_I2C_BYPASS,
@ -175,9 +176,7 @@ static void ps8640_post_disable(struct drm_bridge *bridge)
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
int ret;
ret = ps8640_bridge_vdo_control(ps_bridge, DISABLE);
if (ret < 0)
DRM_ERROR("failed to disable VDO: %d\n", ret);
ps8640_bridge_vdo_control(ps_bridge, DISABLE);
gpiod_set_value(ps_bridge->gpio_reset, 1);
gpiod_set_value(ps_bridge->gpio_powerdown, 1);
@ -200,6 +199,10 @@ static int ps8640_bridge_attach(struct drm_bridge *bridge,
.channel = 0,
.node = NULL,
};
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
/* port@0 is ps8640 dsi input port */
in_ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
if (!in_ep)
@ -242,8 +245,18 @@ static int ps8640_bridge_attach(struct drm_bridge *bridge,
return ret;
}
static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
return drm_get_edid(connector,
ps_bridge->page[PAGE0_DP_CNTL]->adapter);
}
static const struct drm_bridge_funcs ps8640_bridge_funcs = {
.attach = ps8640_bridge_attach,
.get_edid = ps8640_bridge_get_edid,
.post_disable = ps8640_post_disable,
.pre_enable = ps8640_pre_enable,
};
@ -294,6 +307,8 @@ static int ps8640_probe(struct i2c_client *client)
ps_bridge->bridge.funcs = &ps8640_bridge_funcs;
ps_bridge->bridge.of_node = dev->of_node;
ps_bridge->bridge.ops = DRM_BRIDGE_OP_EDID;
ps_bridge->bridge.type = DRM_MODE_CONNECTOR_eDP;
ps_bridge->page[PAGE0_DP_CNTL] = client;

View file

@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 Marek Vasut <marex@denx.de>
*
* Based on tc358764.c by
* Andrzej Hajda <a.hajda@samsung.com>
* Maciej Purski <m.purski@samsung.com>
*
* Based on rpi_touchscreen.c by
* Eric Anholt <eric@anholt.net>
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <video/mipi_display.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
/* PPI layer registers */
#define PPI_STARTPPI 0x0104 /* START control bit */
#define PPI_LPTXTIMECNT 0x0114 /* LPTX timing signal */
#define PPI_D0S_ATMR 0x0144
#define PPI_D1S_ATMR 0x0148
#define PPI_D0S_CLRSIPOCOUNT 0x0164 /* Assertion timer for Lane 0 */
#define PPI_D1S_CLRSIPOCOUNT 0x0168 /* Assertion timer for Lane 1 */
#define PPI_START_FUNCTION 1
/* DSI layer registers */
#define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX */
#define DSI_LANEENABLE 0x0210 /* Enables each lane */
#define DSI_RX_START 1
/* LCDC/DPI Host Registers */
#define LCDCTRL 0x0420
/* SPI Master Registers */
#define SPICMR 0x0450
#define SPITCR 0x0454
/* System Controller Registers */
#define SYSCTRL 0x0464
/* System registers */
#define LPX_PERIOD 3
/* Lane enable PPI and DSI register bits */
#define LANEENABLE_CLEN BIT(0)
#define LANEENABLE_L0EN BIT(1)
#define LANEENABLE_L1EN BIT(2)
struct tc358762 {
struct device *dev;
struct drm_bridge bridge;
struct drm_connector connector;
struct regulator *regulator;
struct drm_bridge *panel_bridge;
bool pre_enabled;
int error;
};
static int tc358762_clear_error(struct tc358762 *ctx)
{
int ret = ctx->error;
ctx->error = 0;
return ret;
}
static void tc358762_write(struct tc358762 *ctx, u16 addr, u32 val)
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
ssize_t ret;
u8 data[6];
if (ctx->error)
return;
data[0] = addr;
data[1] = addr >> 8;
data[2] = val;
data[3] = val >> 8;
data[4] = val >> 16;
data[5] = val >> 24;
ret = mipi_dsi_generic_write(dsi, data, sizeof(data));
if (ret < 0)
ctx->error = ret;
}
static inline struct tc358762 *bridge_to_tc358762(struct drm_bridge *bridge)
{
return container_of(bridge, struct tc358762, bridge);
}
static int tc358762_init(struct tc358762 *ctx)
{
tc358762_write(ctx, DSI_LANEENABLE,
LANEENABLE_L0EN | LANEENABLE_CLEN);
tc358762_write(ctx, PPI_D0S_CLRSIPOCOUNT, 5);
tc358762_write(ctx, PPI_D1S_CLRSIPOCOUNT, 5);
tc358762_write(ctx, PPI_D0S_ATMR, 0);
tc358762_write(ctx, PPI_D1S_ATMR, 0);
tc358762_write(ctx, PPI_LPTXTIMECNT, LPX_PERIOD);
tc358762_write(ctx, SPICMR, 0x00);
tc358762_write(ctx, LCDCTRL, 0x00100150);
tc358762_write(ctx, SYSCTRL, 0x040f);
msleep(100);
tc358762_write(ctx, PPI_STARTPPI, PPI_START_FUNCTION);
tc358762_write(ctx, DSI_STARTDSI, DSI_RX_START);
msleep(100);
return tc358762_clear_error(ctx);
}
static void tc358762_post_disable(struct drm_bridge *bridge)
{
struct tc358762 *ctx = bridge_to_tc358762(bridge);
int ret;
/*
* The post_disable hook might be called multiple times.
* We want to avoid regulator imbalance below.
*/
if (!ctx->pre_enabled)
return;
ctx->pre_enabled = false;
ret = regulator_disable(ctx->regulator);
if (ret < 0)
dev_err(ctx->dev, "error disabling regulators (%d)\n", ret);
}
static void tc358762_pre_enable(struct drm_bridge *bridge)
{
struct tc358762 *ctx = bridge_to_tc358762(bridge);
int ret;
ret = regulator_enable(ctx->regulator);
if (ret < 0)
dev_err(ctx->dev, "error enabling regulators (%d)\n", ret);
ret = tc358762_init(ctx);
if (ret < 0)
dev_err(ctx->dev, "error initializing bridge (%d)\n", ret);
ctx->pre_enabled = true;
}
static int tc358762_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct tc358762 *ctx = bridge_to_tc358762(bridge);
return drm_bridge_attach(bridge->encoder, ctx->panel_bridge,
bridge, flags);
}
static const struct drm_bridge_funcs tc358762_bridge_funcs = {
.post_disable = tc358762_post_disable,
.pre_enable = tc358762_pre_enable,
.attach = tc358762_attach,
};
static int tc358762_parse_dt(struct tc358762 *ctx)
{
struct drm_bridge *panel_bridge;
struct device *dev = ctx->dev;
struct drm_panel *panel;
int ret;
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
if (ret)
return ret;
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
if (IS_ERR(panel_bridge))
return PTR_ERR(panel_bridge);
ctx->panel_bridge = panel_bridge;
return 0;
}
static int tc358762_configure_regulators(struct tc358762 *ctx)
{
ctx->regulator = devm_regulator_get(ctx->dev, "vddc");
if (IS_ERR(ctx->regulator))
return PTR_ERR(ctx->regulator);
return 0;
}
static int tc358762_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct tc358762 *ctx;
int ret;
ctx = devm_kzalloc(dev, sizeof(struct tc358762), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dev = dev;
ctx->pre_enabled = false;
/* TODO: Find out how to get dual-lane mode working */
dsi->lanes = 1;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
MIPI_DSI_MODE_LPM;
ret = tc358762_parse_dt(ctx);
if (ret < 0)
return ret;
ret = tc358762_configure_regulators(ctx);
if (ret < 0)
return ret;
ctx->bridge.funcs = &tc358762_bridge_funcs;
ctx->bridge.type = DRM_MODE_CONNECTOR_DPI;
ctx->bridge.of_node = dev->of_node;
drm_bridge_add(&ctx->bridge);
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
drm_bridge_remove(&ctx->bridge);
dev_err(dev, "failed to attach dsi\n");
}
return ret;
}
static int tc358762_remove(struct mipi_dsi_device *dsi)
{
struct tc358762 *ctx = mipi_dsi_get_drvdata(dsi);
mipi_dsi_detach(dsi);
drm_bridge_remove(&ctx->bridge);
return 0;
}
static const struct of_device_id tc358762_of_match[] = {
{ .compatible = "toshiba,tc358762" },
{ }
};
MODULE_DEVICE_TABLE(of, tc358762_of_match);
static struct mipi_dsi_driver tc358762_driver = {
.probe = tc358762_probe,
.remove = tc358762_remove,
.driver = {
.name = "tc358762",
.of_match_table = tc358762_of_match,
},
};
module_mipi_dsi_driver(tc358762_driver);
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_DESCRIPTION("MIPI-DSI based Driver for TC358762 DSI/DPI Bridge");
MODULE_LICENSE("GPL v2");

View file

@ -153,10 +153,9 @@ static const char * const tc358764_supplies[] = {
struct tc358764 {
struct device *dev;
struct drm_bridge bridge;
struct drm_connector connector;
struct regulator_bulk_data supplies[ARRAY_SIZE(tc358764_supplies)];
struct gpio_desc *gpio_reset;
struct drm_panel *panel;
struct drm_bridge *panel_bridge;
int error;
};
@ -210,12 +209,6 @@ static inline struct tc358764 *bridge_to_tc358764(struct drm_bridge *bridge)
return container_of(bridge, struct tc358764, bridge);
}
static inline
struct tc358764 *connector_to_tc358764(struct drm_connector *connector)
{
return container_of(connector, struct tc358764, connector);
}
static int tc358764_init(struct tc358764 *ctx)
{
u32 v = 0;
@ -278,43 +271,11 @@ static void tc358764_reset(struct tc358764 *ctx)
usleep_range(1000, 2000);
}
static int tc358764_get_modes(struct drm_connector *connector)
{
struct tc358764 *ctx = connector_to_tc358764(connector);
return drm_panel_get_modes(ctx->panel, connector);
}
static const
struct drm_connector_helper_funcs tc358764_connector_helper_funcs = {
.get_modes = tc358764_get_modes,
};
static const struct drm_connector_funcs tc358764_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static void tc358764_disable(struct drm_bridge *bridge)
{
struct tc358764 *ctx = bridge_to_tc358764(bridge);
int ret = drm_panel_disable(bridge_to_tc358764(bridge)->panel);
if (ret < 0)
dev_err(ctx->dev, "error disabling panel (%d)\n", ret);
}
static void tc358764_post_disable(struct drm_bridge *bridge)
{
struct tc358764 *ctx = bridge_to_tc358764(bridge);
int ret;
ret = drm_panel_unprepare(ctx->panel);
if (ret < 0)
dev_err(ctx->dev, "error unpreparing panel (%d)\n", ret);
tc358764_reset(ctx);
usleep_range(10000, 15000);
ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
@ -335,73 +296,28 @@ static void tc358764_pre_enable(struct drm_bridge *bridge)
ret = tc358764_init(ctx);
if (ret < 0)
dev_err(ctx->dev, "error initializing bridge (%d)\n", ret);
ret = drm_panel_prepare(ctx->panel);
if (ret < 0)
dev_err(ctx->dev, "error preparing panel (%d)\n", ret);
}
static void tc358764_enable(struct drm_bridge *bridge)
{
struct tc358764 *ctx = bridge_to_tc358764(bridge);
int ret = drm_panel_enable(ctx->panel);
if (ret < 0)
dev_err(ctx->dev, "error enabling panel (%d)\n", ret);
}
static int tc358764_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct tc358764 *ctx = bridge_to_tc358764(bridge);
struct drm_device *drm = bridge->dev;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
}
ctx->connector.polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(drm, &ctx->connector,
&tc358764_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
if (ret) {
DRM_ERROR("Failed to initialize connector\n");
return ret;
}
drm_connector_helper_add(&ctx->connector,
&tc358764_connector_helper_funcs);
drm_connector_attach_encoder(&ctx->connector, bridge->encoder);
drm_panel_attach(ctx->panel, &ctx->connector);
ctx->connector.funcs->reset(&ctx->connector);
drm_connector_register(&ctx->connector);
return 0;
}
static void tc358764_detach(struct drm_bridge *bridge)
{
struct tc358764 *ctx = bridge_to_tc358764(bridge);
drm_connector_unregister(&ctx->connector);
drm_panel_detach(ctx->panel);
ctx->panel = NULL;
drm_connector_put(&ctx->connector);
return drm_bridge_attach(bridge->encoder, ctx->panel_bridge,
bridge, flags);
}
static const struct drm_bridge_funcs tc358764_bridge_funcs = {
.disable = tc358764_disable,
.post_disable = tc358764_post_disable,
.enable = tc358764_enable,
.pre_enable = tc358764_pre_enable,
.attach = tc358764_attach,
.detach = tc358764_detach,
};
static int tc358764_parse_dt(struct tc358764 *ctx)
{
struct drm_bridge *panel_bridge;
struct device *dev = ctx->dev;
struct drm_panel *panel;
int ret;
ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
@ -410,12 +326,16 @@ static int tc358764_parse_dt(struct tc358764 *ctx)
return PTR_ERR(ctx->gpio_reset);
}
ret = drm_of_find_panel_or_bridge(ctx->dev->of_node, 1, 0, &ctx->panel,
NULL);
if (ret && ret != -EPROBE_DEFER)
dev_err(dev, "cannot find panel (%d)\n", ret);
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
if (ret)
return ret;
return ret;
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
if (IS_ERR(panel_bridge))
return PTR_ERR(panel_bridge);
ctx->panel_bridge = panel_bridge;
return 0;
}
static int tc358764_configure_regulators(struct tc358764 *ctx)
@ -461,6 +381,7 @@ static int tc358764_probe(struct mipi_dsi_device *dsi)
return ret;
ctx->bridge.funcs = &tc358764_bridge_funcs;
ctx->bridge.type = DRM_MODE_CONNECTOR_LVDS;
ctx->bridge.of_node = dev->of_node;
drm_bridge_add(&ctx->bridge);

View file

@ -244,14 +244,12 @@ struct tc_data {
struct drm_dp_aux aux;
struct drm_bridge bridge;
struct drm_bridge *panel_bridge;
struct drm_connector connector;
struct drm_panel *panel;
/* link settings */
struct tc_edp_link link;
/* display edid */
struct edid *edid;
/* current mode */
struct drm_display_mode mode;
@ -1236,13 +1234,6 @@ static int tc_stream_disable(struct tc_data *tc)
return 0;
}
static void tc_bridge_pre_enable(struct drm_bridge *bridge)
{
struct tc_data *tc = bridge_to_tc(bridge);
drm_panel_prepare(tc->panel);
}
static void tc_bridge_enable(struct drm_bridge *bridge)
{
struct tc_data *tc = bridge_to_tc(bridge);
@ -1266,8 +1257,6 @@ static void tc_bridge_enable(struct drm_bridge *bridge)
tc_main_link_disable(tc);
return;
}
drm_panel_enable(tc->panel);
}
static void tc_bridge_disable(struct drm_bridge *bridge)
@ -1275,8 +1264,6 @@ static void tc_bridge_disable(struct drm_bridge *bridge)
struct tc_data *tc = bridge_to_tc(bridge);
int ret;
drm_panel_disable(tc->panel);
ret = tc_stream_disable(tc);
if (ret < 0)
dev_err(tc->dev, "main link stream stop error: %d\n", ret);
@ -1286,13 +1273,6 @@ static void tc_bridge_disable(struct drm_bridge *bridge)
dev_err(tc->dev, "main link disable error: %d\n", ret);
}
static void tc_bridge_post_disable(struct drm_bridge *bridge)
{
struct tc_data *tc = bridge_to_tc(bridge);
drm_panel_unprepare(tc->panel);
}
static bool tc_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adj)
@ -1335,11 +1315,19 @@ static void tc_bridge_mode_set(struct drm_bridge *bridge,
tc->mode = *mode;
}
static struct edid *tc_get_edid(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct tc_data *tc = bridge_to_tc(bridge);
return drm_get_edid(connector, &tc->aux.ddc);
}
static int tc_connector_get_modes(struct drm_connector *connector)
{
struct tc_data *tc = connector_to_tc(connector);
int num_modes;
struct edid *edid;
int count;
int ret;
ret = tc_get_display_props(tc);
@ -1348,42 +1336,30 @@ static int tc_connector_get_modes(struct drm_connector *connector)
return 0;
}
count = drm_panel_get_modes(tc->panel, connector);
if (count > 0)
return count;
if (tc->panel_bridge) {
num_modes = drm_bridge_get_modes(tc->panel_bridge, connector);
if (num_modes > 0)
return num_modes;
}
edid = drm_get_edid(connector, &tc->aux.ddc);
edid = tc_get_edid(&tc->bridge, connector);
num_modes = drm_add_edid_modes(connector, edid);
kfree(edid);
kfree(tc->edid);
tc->edid = edid;
if (!edid)
return 0;
drm_connector_update_edid_property(connector, edid);
count = drm_add_edid_modes(connector, edid);
return count;
return num_modes;
}
static const struct drm_connector_helper_funcs tc_connector_helper_funcs = {
.get_modes = tc_connector_get_modes,
};
static enum drm_connector_status tc_connector_detect(struct drm_connector *connector,
bool force)
static enum drm_connector_status tc_bridge_detect(struct drm_bridge *bridge)
{
struct tc_data *tc = connector_to_tc(connector);
struct tc_data *tc = bridge_to_tc(bridge);
bool conn;
u32 val;
int ret;
if (tc->hpd_pin < 0) {
if (tc->panel)
return connector_status_connected;
else
return connector_status_unknown;
}
ret = regmap_read(tc->regmap, GPIOI, &val);
if (ret)
return connector_status_unknown;
@ -1396,6 +1372,20 @@ static enum drm_connector_status tc_connector_detect(struct drm_connector *conne
return connector_status_disconnected;
}
static enum drm_connector_status
tc_connector_detect(struct drm_connector *connector, bool force)
{
struct tc_data *tc = connector_to_tc(connector);
if (tc->hpd_pin >= 0)
return tc_bridge_detect(&tc->bridge);
if (tc->panel_bridge)
return connector_status_connected;
else
return connector_status_unknown;
}
static const struct drm_connector_funcs tc_connector_funcs = {
.detect = tc_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
@ -1413,16 +1403,20 @@ static int tc_bridge_attach(struct drm_bridge *bridge,
struct drm_device *drm = bridge->dev;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
DRM_ERROR("Fix bridge driver to make connector optional!");
return -EINVAL;
if (tc->panel_bridge) {
/* If a connector is required then this driver shall create it */
ret = drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge,
&tc->bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return ret;
}
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
/* Create DP/eDP connector */
drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs);
ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs,
tc->panel ? DRM_MODE_CONNECTOR_eDP :
DRM_MODE_CONNECTOR_DisplayPort);
ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs, tc->bridge.type);
if (ret)
return ret;
@ -1435,9 +1429,6 @@ static int tc_bridge_attach(struct drm_bridge *bridge,
DRM_CONNECTOR_POLL_DISCONNECT;
}
if (tc->panel)
drm_panel_attach(tc->panel, &tc->connector);
drm_display_info_set_bus_formats(&tc->connector.display_info,
&bus_format, 1);
tc->connector.display_info.bus_flags =
@ -1453,11 +1444,11 @@ static const struct drm_bridge_funcs tc_bridge_funcs = {
.attach = tc_bridge_attach,
.mode_valid = tc_mode_valid,
.mode_set = tc_bridge_mode_set,
.pre_enable = tc_bridge_pre_enable,
.enable = tc_bridge_enable,
.disable = tc_bridge_disable,
.post_disable = tc_bridge_post_disable,
.mode_fixup = tc_bridge_mode_fixup,
.detect = tc_bridge_detect,
.get_edid = tc_get_edid,
};
static bool tc_readable_reg(struct device *dev, unsigned int reg)
@ -1547,6 +1538,7 @@ static irqreturn_t tc_irq_handler(int irq, void *arg)
static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct drm_panel *panel;
struct tc_data *tc;
int ret;
@ -1557,10 +1549,23 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
tc->dev = dev;
/* port@2 is the output port */
ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &tc->panel, NULL);
ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL);
if (ret && ret != -ENODEV)
return ret;
if (panel) {
struct drm_bridge *panel_bridge;
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
if (IS_ERR(panel_bridge))
return PTR_ERR(panel_bridge);
tc->panel_bridge = panel_bridge;
tc->bridge.type = DRM_MODE_CONNECTOR_eDP;
} else {
tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
}
/* Shut down GPIO is optional */
tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
if (IS_ERR(tc->sd_gpio))
@ -1680,6 +1685,10 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
return ret;
tc->bridge.funcs = &tc_bridge_funcs;
if (tc->hpd_pin >= 0)
tc->bridge.ops |= DRM_BRIDGE_OP_DETECT;
tc->bridge.ops |= DRM_BRIDGE_OP_EDID;
tc->bridge.of_node = dev->of_node;
drm_bridge_add(&tc->bridge);

View file

@ -0,0 +1,749 @@
// SPDX-License-Identifier: GPL-2.0
/*
* TC358775 DSI to LVDS bridge driver
*
* Copyright (C) 2020 SMART Wireless Computing
* Author: Vinay Simha BN <simhavcs@gmail.com>
*
*/
/* #define DEBUG */
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
#define FLD_VAL(val, start, end) FIELD_PREP(GENMASK(start, end), val)
/* Registers */
/* DSI D-PHY Layer Registers */
#define D0W_DPHYCONTTX 0x0004 /* Data Lane 0 DPHY Tx Control */
#define CLW_DPHYCONTRX 0x0020 /* Clock Lane DPHY Rx Control */
#define D0W_DPHYCONTRX 0x0024 /* Data Lane 0 DPHY Rx Control */
#define D1W_DPHYCONTRX 0x0028 /* Data Lane 1 DPHY Rx Control */
#define D2W_DPHYCONTRX 0x002C /* Data Lane 2 DPHY Rx Control */
#define D3W_DPHYCONTRX 0x0030 /* Data Lane 3 DPHY Rx Control */
#define COM_DPHYCONTRX 0x0038 /* DPHY Rx Common Control */
#define CLW_CNTRL 0x0040 /* Clock Lane Control */
#define D0W_CNTRL 0x0044 /* Data Lane 0 Control */
#define D1W_CNTRL 0x0048 /* Data Lane 1 Control */
#define D2W_CNTRL 0x004C /* Data Lane 2 Control */
#define D3W_CNTRL 0x0050 /* Data Lane 3 Control */
#define DFTMODE_CNTRL 0x0054 /* DFT Mode Control */
/* DSI PPI Layer Registers */
#define PPI_STARTPPI 0x0104 /* START control bit of PPI-TX function. */
#define PPI_START_FUNCTION 1
#define PPI_BUSYPPI 0x0108
#define PPI_LINEINITCNT 0x0110 /* Line Initialization Wait Counter */
#define PPI_LPTXTIMECNT 0x0114
#define PPI_LANEENABLE 0x0134 /* Enables each lane at the PPI layer. */
#define PPI_TX_RX_TA 0x013C /* DSI Bus Turn Around timing parameters */
/* Analog timer function enable */
#define PPI_CLS_ATMR 0x0140 /* Delay for Clock Lane in LPRX */
#define PPI_D0S_ATMR 0x0144 /* Delay for Data Lane 0 in LPRX */
#define PPI_D1S_ATMR 0x0148 /* Delay for Data Lane 1 in LPRX */
#define PPI_D2S_ATMR 0x014C /* Delay for Data Lane 2 in LPRX */
#define PPI_D3S_ATMR 0x0150 /* Delay for Data Lane 3 in LPRX */
#define PPI_D0S_CLRSIPOCOUNT 0x0164 /* For lane 0 */
#define PPI_D1S_CLRSIPOCOUNT 0x0168 /* For lane 1 */
#define PPI_D2S_CLRSIPOCOUNT 0x016C /* For lane 2 */
#define PPI_D3S_CLRSIPOCOUNT 0x0170 /* For lane 3 */
#define CLS_PRE 0x0180 /* Digital Counter inside of PHY IO */
#define D0S_PRE 0x0184 /* Digital Counter inside of PHY IO */
#define D1S_PRE 0x0188 /* Digital Counter inside of PHY IO */
#define D2S_PRE 0x018C /* Digital Counter inside of PHY IO */
#define D3S_PRE 0x0190 /* Digital Counter inside of PHY IO */
#define CLS_PREP 0x01A0 /* Digital Counter inside of PHY IO */
#define D0S_PREP 0x01A4 /* Digital Counter inside of PHY IO */
#define D1S_PREP 0x01A8 /* Digital Counter inside of PHY IO */
#define D2S_PREP 0x01AC /* Digital Counter inside of PHY IO */
#define D3S_PREP 0x01B0 /* Digital Counter inside of PHY IO */
#define CLS_ZERO 0x01C0 /* Digital Counter inside of PHY IO */
#define D0S_ZERO 0x01C4 /* Digital Counter inside of PHY IO */
#define D1S_ZERO 0x01C8 /* Digital Counter inside of PHY IO */
#define D2S_ZERO 0x01CC /* Digital Counter inside of PHY IO */
#define D3S_ZERO 0x01D0 /* Digital Counter inside of PHY IO */
#define PPI_CLRFLG 0x01E0 /* PRE Counters has reached set values */
#define PPI_CLRSIPO 0x01E4 /* Clear SIPO values, Slave mode use only. */
#define HSTIMEOUT 0x01F0 /* HS Rx Time Out Counter */
#define HSTIMEOUTENABLE 0x01F4 /* Enable HS Rx Time Out Counter */
#define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX function */
#define DSI_RX_START 1
#define DSI_BUSYDSI 0x0208
#define DSI_LANEENABLE 0x0210 /* Enables each lane at the Protocol layer. */
#define DSI_LANESTATUS0 0x0214 /* Displays lane is in HS RX mode. */
#define DSI_LANESTATUS1 0x0218 /* Displays lane is in ULPS or STOP state */
#define DSI_INTSTATUS 0x0220 /* Interrupt Status */
#define DSI_INTMASK 0x0224 /* Interrupt Mask */
#define DSI_INTCLR 0x0228 /* Interrupt Clear */
#define DSI_LPTXTO 0x0230 /* Low Power Tx Time Out Counter */
#define DSIERRCNT 0x0300 /* DSI Error Count */
#define APLCTRL 0x0400 /* Application Layer Control */
#define RDPKTLN 0x0404 /* Command Read Packet Length */
#define VPCTRL 0x0450 /* Video Path Control */
#define HTIM1 0x0454 /* Horizontal Timing Control 1 */
#define HTIM2 0x0458 /* Horizontal Timing Control 2 */
#define VTIM1 0x045C /* Vertical Timing Control 1 */
#define VTIM2 0x0460 /* Vertical Timing Control 2 */
#define VFUEN 0x0464 /* Video Frame Timing Update Enable */
#define VFUEN_EN BIT(0) /* Upload Enable */
/* Mux Input Select for LVDS LINK Input */
#define LV_MX0003 0x0480 /* Bit 0 to 3 */
#define LV_MX0407 0x0484 /* Bit 4 to 7 */
#define LV_MX0811 0x0488 /* Bit 8 to 11 */
#define LV_MX1215 0x048C /* Bit 12 to 15 */
#define LV_MX1619 0x0490 /* Bit 16 to 19 */
#define LV_MX2023 0x0494 /* Bit 20 to 23 */
#define LV_MX2427 0x0498 /* Bit 24 to 27 */
#define LV_MX(b0, b1, b2, b3) (FLD_VAL(b0, 4, 0) | FLD_VAL(b1, 12, 8) | \
FLD_VAL(b2, 20, 16) | FLD_VAL(b3, 28, 24))
/* Input bit numbers used in mux registers */
enum {
LVI_R0,
LVI_R1,
LVI_R2,
LVI_R3,
LVI_R4,
LVI_R5,
LVI_R6,
LVI_R7,
LVI_G0,
LVI_G1,
LVI_G2,
LVI_G3,
LVI_G4,
LVI_G5,
LVI_G6,
LVI_G7,
LVI_B0,
LVI_B1,
LVI_B2,
LVI_B3,
LVI_B4,
LVI_B5,
LVI_B6,
LVI_B7,
LVI_HS,
LVI_VS,
LVI_DE,
LVI_L0
};
#define LVCFG 0x049C /* LVDS Configuration */
#define LVPHY0 0x04A0 /* LVDS PHY 0 */
#define LV_PHY0_RST(v) FLD_VAL(v, 22, 22) /* PHY reset */
#define LV_PHY0_IS(v) FLD_VAL(v, 15, 14)
#define LV_PHY0_ND(v) FLD_VAL(v, 4, 0) /* Frequency range select */
#define LV_PHY0_PRBS_ON(v) FLD_VAL(v, 20, 16) /* Clock/Data Flag pins */
#define LVPHY1 0x04A4 /* LVDS PHY 1 */
#define SYSSTAT 0x0500 /* System Status */
#define SYSRST 0x0504 /* System Reset */
#define SYS_RST_I2CS BIT(0) /* Reset I2C-Slave controller */
#define SYS_RST_I2CM BIT(1) /* Reset I2C-Master controller */
#define SYS_RST_LCD BIT(2) /* Reset LCD controller */
#define SYS_RST_BM BIT(3) /* Reset Bus Management controller */
#define SYS_RST_DSIRX BIT(4) /* Reset DSI-RX and App controller */
#define SYS_RST_REG BIT(5) /* Reset Register module */
/* GPIO Registers */
#define GPIOC 0x0520 /* GPIO Control */
#define GPIOO 0x0524 /* GPIO Output */
#define GPIOI 0x0528 /* GPIO Input */
/* I2C Registers */
#define I2CTIMCTRL 0x0540 /* I2C IF Timing and Enable Control */
#define I2CMADDR 0x0544 /* I2C Master Addressing */
#define WDATAQ 0x0548 /* Write Data Queue */
#define RDATAQ 0x054C /* Read Data Queue */
/* Chip ID and Revision ID Register */
#define IDREG 0x0580
#define LPX_PERIOD 4
#define TTA_GET 0x40000
#define TTA_SURE 6
#define SINGLE_LINK 1
#define DUAL_LINK 2
#define TC358775XBG_ID 0x00007500
/* Debug Registers */
#define DEBUG00 0x05A0 /* Debug */
#define DEBUG01 0x05A4 /* LVDS Data */
#define DSI_CLEN_BIT BIT(0)
#define DIVIDE_BY_3 3 /* PCLK=DCLK/3 */
#define DIVIDE_BY_6 6 /* PCLK=DCLK/6 */
#define LVCFG_LVEN_BIT BIT(0)
#define L0EN BIT(1)
#define TC358775_VPCTRL_VSDELAY__MASK 0x3FF00000
#define TC358775_VPCTRL_VSDELAY__SHIFT 20
static inline u32 TC358775_VPCTRL_VSDELAY(uint32_t val)
{
return ((val) << TC358775_VPCTRL_VSDELAY__SHIFT) &
TC358775_VPCTRL_VSDELAY__MASK;
}
#define TC358775_VPCTRL_OPXLFMT__MASK 0x00000100
#define TC358775_VPCTRL_OPXLFMT__SHIFT 8
static inline u32 TC358775_VPCTRL_OPXLFMT(uint32_t val)
{
return ((val) << TC358775_VPCTRL_OPXLFMT__SHIFT) &
TC358775_VPCTRL_OPXLFMT__MASK;
}
#define TC358775_VPCTRL_MSF__MASK 0x00000001
#define TC358775_VPCTRL_MSF__SHIFT 0
static inline u32 TC358775_VPCTRL_MSF(uint32_t val)
{
return ((val) << TC358775_VPCTRL_MSF__SHIFT) &
TC358775_VPCTRL_MSF__MASK;
}
#define TC358775_LVCFG_PCLKDIV__MASK 0x000000f0
#define TC358775_LVCFG_PCLKDIV__SHIFT 4
static inline u32 TC358775_LVCFG_PCLKDIV(uint32_t val)
{
return ((val) << TC358775_LVCFG_PCLKDIV__SHIFT) &
TC358775_LVCFG_PCLKDIV__MASK;
}
#define TC358775_LVCFG_LVDLINK__MASK 0x00000002
#define TC358775_LVCFG_LVDLINK__SHIFT 0
static inline u32 TC358775_LVCFG_LVDLINK(uint32_t val)
{
return ((val) << TC358775_LVCFG_LVDLINK__SHIFT) &
TC358775_LVCFG_LVDLINK__MASK;
}
enum tc358775_ports {
TC358775_DSI_IN,
TC358775_LVDS_OUT0,
TC358775_LVDS_OUT1,
};
struct tc_data {
struct i2c_client *i2c;
struct device *dev;
struct drm_bridge bridge;
struct drm_bridge *panel_bridge;
struct device_node *host_node;
struct mipi_dsi_device *dsi;
u8 num_dsi_lanes;
struct regulator *vdd;
struct regulator *vddio;
struct gpio_desc *reset_gpio;
struct gpio_desc *stby_gpio;
u8 lvds_link; /* single-link or dual-link */
u8 bpc;
};
static inline struct tc_data *bridge_to_tc(struct drm_bridge *b)
{
return container_of(b, struct tc_data, bridge);
}
static void tc_bridge_pre_enable(struct drm_bridge *bridge)
{
struct tc_data *tc = bridge_to_tc(bridge);
struct device *dev = &tc->dsi->dev;
int ret;
ret = regulator_enable(tc->vddio);
if (ret < 0)
dev_err(dev, "regulator vddio enable failed, %d\n", ret);
usleep_range(10000, 11000);
ret = regulator_enable(tc->vdd);
if (ret < 0)
dev_err(dev, "regulator vdd enable failed, %d\n", ret);
usleep_range(10000, 11000);
gpiod_set_value(tc->stby_gpio, 0);
usleep_range(10000, 11000);
gpiod_set_value(tc->reset_gpio, 0);
usleep_range(10, 20);
}
static void tc_bridge_post_disable(struct drm_bridge *bridge)
{
struct tc_data *tc = bridge_to_tc(bridge);
struct device *dev = &tc->dsi->dev;
int ret;
gpiod_set_value(tc->reset_gpio, 1);
usleep_range(10, 20);
gpiod_set_value(tc->stby_gpio, 1);
usleep_range(10000, 11000);
ret = regulator_disable(tc->vdd);
if (ret < 0)
dev_err(dev, "regulator vdd disable failed, %d\n", ret);
usleep_range(10000, 11000);
ret = regulator_disable(tc->vddio);
if (ret < 0)
dev_err(dev, "regulator vddio disable failed, %d\n", ret);
usleep_range(10000, 11000);
}
static void d2l_read(struct i2c_client *i2c, u16 addr, u32 *val)
{
int ret;
u8 buf_addr[2];
put_unaligned_be16(addr, buf_addr);
ret = i2c_master_send(i2c, buf_addr, sizeof(buf_addr));
if (ret < 0)
goto fail;
ret = i2c_master_recv(i2c, (u8 *)val, sizeof(*val));
if (ret < 0)
goto fail;
pr_debug("d2l: I2C : addr:%04x value:%08x\n", addr, *val);
fail:
dev_err(&i2c->dev, "Error %d reading from subaddress 0x%x\n",
ret, addr);
}
static void d2l_write(struct i2c_client *i2c, u16 addr, u32 val)
{
u8 data[6];
int ret;
put_unaligned_be16(addr, data);
put_unaligned_le32(val, data + 2);
ret = i2c_master_send(i2c, data, ARRAY_SIZE(data));
if (ret < 0)
dev_err(&i2c->dev, "Error %d writing to subaddress 0x%x\n",
ret, addr);
}
/* helper function to access bus_formats */
static struct drm_connector *get_connector(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder == encoder)
return connector;
return NULL;
}
static void tc_bridge_enable(struct drm_bridge *bridge)
{
struct tc_data *tc = bridge_to_tc(bridge);
u32 hback_porch, hsync_len, hfront_porch, hactive, htime1, htime2;
u32 vback_porch, vsync_len, vfront_porch, vactive, vtime1, vtime2;
u32 val = 0;
u16 dsiclk, clkdiv, byteclk, t1, t2, t3, vsdelay;
struct drm_display_mode *mode;
struct drm_connector *connector = get_connector(bridge->encoder);
mode = &bridge->encoder->crtc->state->adjusted_mode;
hback_porch = mode->htotal - mode->hsync_end;
hsync_len = mode->hsync_end - mode->hsync_start;
vback_porch = mode->vtotal - mode->vsync_end;
vsync_len = mode->vsync_end - mode->vsync_start;
htime1 = (hback_porch << 16) + hsync_len;
vtime1 = (vback_porch << 16) + vsync_len;
hfront_porch = mode->hsync_start - mode->hdisplay;
hactive = mode->hdisplay;
vfront_porch = mode->vsync_start - mode->vdisplay;
vactive = mode->vdisplay;
htime2 = (hfront_porch << 16) + hactive;
vtime2 = (vfront_porch << 16) + vactive;
d2l_read(tc->i2c, IDREG, &val);
dev_info(tc->dev, "DSI2LVDS Chip ID.%02x Revision ID. %02x **\n",
(val >> 8) & 0xFF, val & 0xFF);
d2l_write(tc->i2c, SYSRST, SYS_RST_REG | SYS_RST_DSIRX | SYS_RST_BM |
SYS_RST_LCD | SYS_RST_I2CM | SYS_RST_I2CS);
usleep_range(30000, 40000);
d2l_write(tc->i2c, PPI_TX_RX_TA, TTA_GET | TTA_SURE);
d2l_write(tc->i2c, PPI_LPTXTIMECNT, LPX_PERIOD);
d2l_write(tc->i2c, PPI_D0S_CLRSIPOCOUNT, 3);
d2l_write(tc->i2c, PPI_D1S_CLRSIPOCOUNT, 3);
d2l_write(tc->i2c, PPI_D2S_CLRSIPOCOUNT, 3);
d2l_write(tc->i2c, PPI_D3S_CLRSIPOCOUNT, 3);
val = ((L0EN << tc->num_dsi_lanes) - L0EN) | DSI_CLEN_BIT;
d2l_write(tc->i2c, PPI_LANEENABLE, val);
d2l_write(tc->i2c, DSI_LANEENABLE, val);
d2l_write(tc->i2c, PPI_STARTPPI, PPI_START_FUNCTION);
d2l_write(tc->i2c, DSI_STARTDSI, DSI_RX_START);
if (tc->bpc == 8)
val = TC358775_VPCTRL_OPXLFMT(1);
else /* bpc = 6; */
val = TC358775_VPCTRL_MSF(1);
dsiclk = mode->crtc_clock * 3 * tc->bpc / tc->num_dsi_lanes / 1000;
clkdiv = dsiclk / DIVIDE_BY_3 * tc->lvds_link;
byteclk = dsiclk / 4;
t1 = hactive * (tc->bpc * 3 / 8) / tc->num_dsi_lanes;
t2 = ((100000 / clkdiv)) * (hactive + hback_porch + hsync_len + hfront_porch) / 1000;
t3 = ((t2 * byteclk) / 100) - (hactive * (tc->bpc * 3 / 8) /
tc->num_dsi_lanes);
vsdelay = (clkdiv * (t1 + t3) / byteclk) - hback_porch - hsync_len - hactive;
val |= TC358775_VPCTRL_VSDELAY(vsdelay);
d2l_write(tc->i2c, VPCTRL, val);
d2l_write(tc->i2c, HTIM1, htime1);
d2l_write(tc->i2c, VTIM1, vtime1);
d2l_write(tc->i2c, HTIM2, htime2);
d2l_write(tc->i2c, VTIM2, vtime2);
d2l_write(tc->i2c, VFUEN, VFUEN_EN);
d2l_write(tc->i2c, SYSRST, SYS_RST_LCD);
d2l_write(tc->i2c, LVPHY0, LV_PHY0_PRBS_ON(4) | LV_PHY0_ND(6));
dev_dbg(tc->dev, "bus_formats %04x bpc %d\n",
connector->display_info.bus_formats[0],
tc->bpc);
/*
* Default hardware register settings of tc358775 configured
* with MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA jeida-24 format
*/
if (connector->display_info.bus_formats[0] ==
MEDIA_BUS_FMT_RGB888_1X7X4_SPWG) {
/* VESA-24 */
d2l_write(tc->i2c, LV_MX0003, LV_MX(LVI_R0, LVI_R1, LVI_R2, LVI_R3));
d2l_write(tc->i2c, LV_MX0407, LV_MX(LVI_R4, LVI_R7, LVI_R5, LVI_G0));
d2l_write(tc->i2c, LV_MX0811, LV_MX(LVI_G1, LVI_G2, LVI_G6, LVI_G7));
d2l_write(tc->i2c, LV_MX1215, LV_MX(LVI_G3, LVI_G4, LVI_G5, LVI_B0));
d2l_write(tc->i2c, LV_MX1619, LV_MX(LVI_B6, LVI_B7, LVI_B1, LVI_B2));
d2l_write(tc->i2c, LV_MX2023, LV_MX(LVI_B3, LVI_B4, LVI_B5, LVI_L0));
d2l_write(tc->i2c, LV_MX2427, LV_MX(LVI_HS, LVI_VS, LVI_DE, LVI_R6));
} else { /* MEDIA_BUS_FMT_RGB666_1X7X3_SPWG - JEIDA-18 */
d2l_write(tc->i2c, LV_MX0003, LV_MX(LVI_R0, LVI_R1, LVI_R2, LVI_R3));
d2l_write(tc->i2c, LV_MX0407, LV_MX(LVI_R4, LVI_L0, LVI_R5, LVI_G0));
d2l_write(tc->i2c, LV_MX0811, LV_MX(LVI_G1, LVI_G2, LVI_L0, LVI_L0));
d2l_write(tc->i2c, LV_MX1215, LV_MX(LVI_G3, LVI_G4, LVI_G5, LVI_B0));
d2l_write(tc->i2c, LV_MX1619, LV_MX(LVI_L0, LVI_L0, LVI_B1, LVI_B2));
d2l_write(tc->i2c, LV_MX2023, LV_MX(LVI_B3, LVI_B4, LVI_B5, LVI_L0));
d2l_write(tc->i2c, LV_MX2427, LV_MX(LVI_HS, LVI_VS, LVI_DE, LVI_L0));
}
d2l_write(tc->i2c, VFUEN, VFUEN_EN);
val = LVCFG_LVEN_BIT;
if (tc->lvds_link == DUAL_LINK) {
val |= TC358775_LVCFG_LVDLINK(1);
val |= TC358775_LVCFG_PCLKDIV(DIVIDE_BY_6);
} else {
val |= TC358775_LVCFG_PCLKDIV(DIVIDE_BY_3);
};
d2l_write(tc->i2c, LVCFG, val);
}
static enum drm_mode_status
tc_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct tc_data *tc = bridge_to_tc(bridge);
/*
* Maximum pixel clock speed 135MHz for single-link
* 270MHz for dual-link
*/
if ((mode->clock > 135000 && tc->lvds_link == SINGLE_LINK) ||
(mode->clock > 270000 && tc->lvds_link == DUAL_LINK))
return MODE_CLOCK_HIGH;
switch (info->bus_formats[0]) {
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
/* RGB888 */
tc->bpc = 8;
break;
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
/* RGB666 */
tc->bpc = 6;
break;
default:
dev_warn(tc->dev,
"unsupported LVDS bus format 0x%04x\n",
info->bus_formats[0]);
return MODE_NOMODE;
}
return MODE_OK;
}
static int tc358775_parse_dt(struct device_node *np, struct tc_data *tc)
{
struct device_node *endpoint;
struct device_node *parent;
struct device_node *remote;
struct property *prop;
int len = 0;
/*
* To get the data-lanes of dsi, we need to access the dsi0_out of port1
* of dsi0 endpoint from bridge port0 of d2l_in
*/
endpoint = of_graph_get_endpoint_by_regs(tc->dev->of_node,
TC358775_DSI_IN, -1);
if (endpoint) {
/* dsi0_out node */
parent = of_graph_get_remote_port_parent(endpoint);
of_node_put(endpoint);
if (parent) {
/* dsi0 port 1 */
endpoint = of_graph_get_endpoint_by_regs(parent, 1, -1);
of_node_put(parent);
if (endpoint) {
prop = of_find_property(endpoint, "data-lanes",
&len);
of_node_put(endpoint);
if (!prop) {
dev_err(tc->dev,
"failed to find data lane\n");
return -EPROBE_DEFER;
}
}
}
}
tc->num_dsi_lanes = len / sizeof(u32);
if (tc->num_dsi_lanes < 1 || tc->num_dsi_lanes > 4)
return -EINVAL;
tc->host_node = of_graph_get_remote_node(np, 0, 0);
if (!tc->host_node)
return -ENODEV;
of_node_put(tc->host_node);
tc->lvds_link = SINGLE_LINK;
endpoint = of_graph_get_endpoint_by_regs(tc->dev->of_node,
TC358775_LVDS_OUT1, -1);
if (endpoint) {
remote = of_graph_get_remote_port_parent(endpoint);
of_node_put(endpoint);
if (remote) {
if (of_device_is_available(remote))
tc->lvds_link = DUAL_LINK;
of_node_put(remote);
}
}
dev_dbg(tc->dev, "no.of dsi lanes: %d\n", tc->num_dsi_lanes);
dev_dbg(tc->dev, "operating in %d-link mode\n", tc->lvds_link);
return 0;
}
static int tc_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct tc_data *tc = bridge_to_tc(bridge);
struct device *dev = &tc->i2c->dev;
struct mipi_dsi_host *host;
struct mipi_dsi_device *dsi;
int ret;
const struct mipi_dsi_device_info info = { .type = "tc358775",
.channel = 0,
.node = NULL,
};
host = of_find_mipi_dsi_host_by_node(tc->host_node);
if (!host) {
dev_err(dev, "failed to find dsi host\n");
return -EPROBE_DEFER;
}
dsi = mipi_dsi_device_register_full(host, &info);
if (IS_ERR(dsi)) {
dev_err(dev, "failed to create dsi device\n");
ret = PTR_ERR(dsi);
goto err_dsi_device;
}
tc->dsi = dsi;
dsi->lanes = tc->num_dsi_lanes;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
dev_err(dev, "failed to attach dsi to host\n");
goto err_dsi_attach;
}
/* Attach the panel-bridge to the dsi bridge */
return drm_bridge_attach(bridge->encoder, tc->panel_bridge,
&tc->bridge, flags);
err_dsi_attach:
mipi_dsi_device_unregister(dsi);
err_dsi_device:
return ret;
}
static const struct drm_bridge_funcs tc_bridge_funcs = {
.attach = tc_bridge_attach,
.pre_enable = tc_bridge_pre_enable,
.enable = tc_bridge_enable,
.mode_valid = tc_mode_valid,
.post_disable = tc_bridge_post_disable,
};
static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct drm_panel *panel;
struct tc_data *tc;
int ret;
tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL);
if (!tc)
return -ENOMEM;
tc->dev = dev;
tc->i2c = client;
ret = drm_of_find_panel_or_bridge(dev->of_node, TC358775_LVDS_OUT0,
0, &panel, NULL);
if (ret < 0)
return ret;
if (!panel)
return -ENODEV;
tc->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
if (IS_ERR(tc->panel_bridge))
return PTR_ERR(tc->panel_bridge);
ret = tc358775_parse_dt(dev->of_node, tc);
if (ret)
return ret;
tc->vddio = devm_regulator_get(dev, "vddio-supply");
if (IS_ERR(tc->vddio)) {
ret = PTR_ERR(tc->vddio);
dev_err(dev, "vddio-supply not found\n");
return ret;
}
tc->vdd = devm_regulator_get(dev, "vdd-supply");
if (IS_ERR(tc->vdd)) {
ret = PTR_ERR(tc->vdd);
dev_err(dev, "vdd-supply not found\n");
return ret;
}
tc->stby_gpio = devm_gpiod_get(dev, "stby", GPIOD_OUT_HIGH);
if (IS_ERR(tc->stby_gpio)) {
ret = PTR_ERR(tc->stby_gpio);
dev_err(dev, "cannot get stby-gpio %d\n", ret);
return ret;
}
tc->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(tc->reset_gpio)) {
ret = PTR_ERR(tc->reset_gpio);
dev_err(dev, "cannot get reset-gpios %d\n", ret);
return ret;
}
tc->bridge.funcs = &tc_bridge_funcs;
tc->bridge.of_node = dev->of_node;
drm_bridge_add(&tc->bridge);
i2c_set_clientdata(client, tc);
return 0;
}
static int tc_remove(struct i2c_client *client)
{
struct tc_data *tc = i2c_get_clientdata(client);
drm_bridge_remove(&tc->bridge);
return 0;
}
static const struct i2c_device_id tc358775_i2c_ids[] = {
{ "tc358775", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tc358775_i2c_ids);
static const struct of_device_id tc358775_of_ids[] = {
{ .compatible = "toshiba,tc358775", },
{ }
};
MODULE_DEVICE_TABLE(of, tc358775_of_ids);
static struct i2c_driver tc358775_driver = {
.driver = {
.name = "tc358775",
.of_match_table = tc358775_of_ids,
},
.id_table = tc358775_i2c_ids,
.probe = tc_probe,
.remove = tc_remove,
};
module_i2c_driver(tc358775_driver);
MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
MODULE_DESCRIPTION("TC358775 DSI/LVDS bridge driver");
MODULE_LICENSE("GPL v2");

View file

@ -394,9 +394,6 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge,
}
pdata->dsi = dsi;
/* attach panel to bridge */
drm_panel_attach(pdata->panel, &pdata->connector);
return 0;
err_dsi_attach:

View file

@ -187,6 +187,7 @@ drm_bridge_connector_detect(struct drm_connector *connector, bool force)
case DRM_MODE_CONNECTOR_DPI:
case DRM_MODE_CONNECTOR_LVDS:
case DRM_MODE_CONNECTOR_DSI:
case DRM_MODE_CONNECTOR_eDP:
status = connector_status_connected;
break;
default:

View file

@ -850,7 +850,7 @@ static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I, TV-out and DP */
{ DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */
{ DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */
};
@ -867,7 +867,7 @@ static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I, TV-out and DP */
{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
{ DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */
{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
@ -876,6 +876,19 @@ static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
drm_tv_subconnector_enum_list)
static const struct drm_prop_enum_list drm_dp_subconnector_enum_list[] = {
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I, TV-out and DP */
{ DRM_MODE_SUBCONNECTOR_VGA, "VGA" }, /* DP */
{ DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DP */
{ DRM_MODE_SUBCONNECTOR_HDMIA, "HDMI" }, /* DP */
{ DRM_MODE_SUBCONNECTOR_DisplayPort, "DP" }, /* DP */
{ DRM_MODE_SUBCONNECTOR_Wireless, "Wireless" }, /* DP */
{ DRM_MODE_SUBCONNECTOR_Native, "Native" }, /* DP */
};
DRM_ENUM_NAME_FN(drm_get_dp_subconnector_name,
drm_dp_subconnector_enum_list)
static const struct drm_prop_enum_list hdmi_colorspaces[] = {
/* For Default case, driver will set the colorspace */
{ DRM_MODE_COLORIMETRY_DEFAULT, "Default" },
@ -1217,6 +1230,14 @@ static const struct drm_prop_enum_list dp_colorspaces[] = {
* can also expose this property to external outputs, in which case they
* must support "None", which should be the default (since external screens
* have a built-in scaler).
*
* subconnector:
* This property is used by DVI-I, TVout and DisplayPort to indicate different
* connector subtypes. Enum values more or less match with those from main
* connector types.
* For DVI-I and TVout there is also a matching property "select subconnector"
* allowing to switch between signal types.
* DP subconnector corresponds to a downstream port.
*/
int drm_connector_create_standard_properties(struct drm_device *dev)
@ -1305,6 +1326,30 @@ int drm_mode_create_dvi_i_properties(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
/**
* drm_connector_attach_dp_subconnector_property - create subconnector property for DP
* @connector: drm_connector to attach property
*
* Called by a driver when DP connector is created.
*/
void drm_connector_attach_dp_subconnector_property(struct drm_connector *connector)
{
struct drm_mode_config *mode_config = &connector->dev->mode_config;
if (!mode_config->dp_subconnector_property)
mode_config->dp_subconnector_property =
drm_property_create_enum(connector->dev,
DRM_MODE_PROP_IMMUTABLE,
"subconnector",
drm_dp_subconnector_enum_list,
ARRAY_SIZE(drm_dp_subconnector_enum_list));
drm_object_attach_property(&connector->base,
mode_config->dp_subconnector_property,
DRM_MODE_SUBCONNECTOR_Unknown);
}
EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property);
/**
* DOC: HDMI connector properties
*

View file

@ -597,6 +597,77 @@ void drm_dp_downstream_debug(struct seq_file *m,
}
EXPORT_SYMBOL(drm_dp_downstream_debug);
/**
* drm_dp_subconnector_type() - get DP branch device type
*
*/
enum drm_mode_subconnector
drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
const u8 port_cap[4])
{
int type;
if (!drm_dp_is_branch(dpcd))
return DRM_MODE_SUBCONNECTOR_Native;
/* DP 1.0 approach */
if (dpcd[DP_DPCD_REV] == DP_DPCD_REV_10) {
type = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
DP_DWN_STRM_PORT_TYPE_MASK;
switch (type) {
case DP_DWN_STRM_PORT_TYPE_TMDS:
/* Can be HDMI or DVI-D, DVI-D is a safer option */
return DRM_MODE_SUBCONNECTOR_DVID;
case DP_DWN_STRM_PORT_TYPE_ANALOG:
/* Can be VGA or DVI-A, VGA is more popular */
return DRM_MODE_SUBCONNECTOR_VGA;
case DP_DWN_STRM_PORT_TYPE_DP:
return DRM_MODE_SUBCONNECTOR_DisplayPort;
case DP_DWN_STRM_PORT_TYPE_OTHER:
default:
return DRM_MODE_SUBCONNECTOR_Unknown;
}
}
type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
switch (type) {
case DP_DS_PORT_TYPE_DP:
case DP_DS_PORT_TYPE_DP_DUALMODE:
return DRM_MODE_SUBCONNECTOR_DisplayPort;
case DP_DS_PORT_TYPE_VGA:
return DRM_MODE_SUBCONNECTOR_VGA;
case DP_DS_PORT_TYPE_DVI:
return DRM_MODE_SUBCONNECTOR_DVID;
case DP_DS_PORT_TYPE_HDMI:
return DRM_MODE_SUBCONNECTOR_HDMIA;
case DP_DS_PORT_TYPE_WIRELESS:
return DRM_MODE_SUBCONNECTOR_Wireless;
case DP_DS_PORT_TYPE_NON_EDID:
default:
return DRM_MODE_SUBCONNECTOR_Unknown;
}
}
EXPORT_SYMBOL(drm_dp_subconnector_type);
/**
* drm_mode_set_dp_subconnector_property - set subconnector for DP connector
*
* Called by a driver on every detect event.
*/
void drm_dp_set_subconnector_property(struct drm_connector *connector,
enum drm_connector_status status,
const u8 *dpcd,
const u8 port_cap[4])
{
enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
if (status == connector_status_connected)
subconnector = drm_dp_subconnector_type(dpcd, port_cap);
drm_object_property_set_value(&connector->base,
connector->dev->mode_config.dp_subconnector_property,
subconnector);
}
EXPORT_SYMBOL(drm_dp_set_subconnector_property);
/*
* I2C-over-AUX implementation
*/

View file

@ -653,7 +653,7 @@ static void drm_gem_vram_bo_driver_evict_flags(struct drm_gem_vram_object *gbo,
static void drm_gem_vram_bo_driver_move_notify(struct drm_gem_vram_object *gbo,
bool evict,
struct ttm_mem_reg *new_mem)
struct ttm_resource *new_mem)
{
struct ttm_bo_kmap_obj *kmap = &gbo->kmap;
@ -1004,28 +1004,6 @@ static struct ttm_tt *bo_driver_ttm_tt_create(struct ttm_buffer_object *bo,
return NULL;
}
static int bo_driver_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
struct ttm_mem_type_manager *man)
{
switch (type) {
case TTM_PL_SYSTEM:
man->flags = 0;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_VRAM:
man->func = &ttm_bo_manager_func;
man->flags = TTM_MEMTYPE_FLAG_FIXED;
man->available_caching = TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_WC;
man->default_caching = TTM_PL_FLAG_WC;
break;
default:
return -EINVAL;
}
return 0;
}
static void bo_driver_evict_flags(struct ttm_buffer_object *bo,
struct ttm_placement *placement)
{
@ -1042,7 +1020,7 @@ static void bo_driver_evict_flags(struct ttm_buffer_object *bo,
static void bo_driver_move_notify(struct ttm_buffer_object *bo,
bool evict,
struct ttm_mem_reg *new_mem)
struct ttm_resource *new_mem)
{
struct drm_gem_vram_object *gbo;
@ -1056,18 +1034,12 @@ static void bo_driver_move_notify(struct ttm_buffer_object *bo,
}
static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev,
struct ttm_mem_reg *mem)
struct ttm_resource *mem)
{
struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bdev);
mem->bus.addr = NULL;
mem->bus.size = mem->num_pages << PAGE_SHIFT;
switch (mem->mem_type) {
case TTM_PL_SYSTEM: /* nothing to do */
mem->bus.offset = 0;
mem->bus.base = 0;
mem->bus.is_iomem = false;
break;
case TTM_PL_VRAM:
mem->bus.offset = mem->start << PAGE_SHIFT;
@ -1083,9 +1055,6 @@ static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev,
static struct ttm_bo_driver bo_driver = {
.ttm_tt_create = bo_driver_ttm_tt_create,
.ttm_tt_populate = ttm_pool_populate,
.ttm_tt_unpopulate = ttm_pool_unpopulate,
.init_mem_type = bo_driver_init_mem_type,
.eviction_valuable = ttm_bo_eviction_valuable,
.evict_flags = bo_driver_evict_flags,
.move_notify = bo_driver_move_notify,
@ -1100,12 +1069,10 @@ static int drm_vram_mm_debugfs(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_vram_mm *vmm = node->minor->dev->vram_mm;
struct drm_mm *mm = vmm->bdev.man[TTM_PL_VRAM].priv;
struct ttm_resource_manager *man = ttm_manager_type(&vmm->bdev, TTM_PL_VRAM);
struct drm_printer p = drm_seq_file_printer(m);
spin_lock(&ttm_bo_glob.lru_lock);
drm_mm_print(mm, &p);
spin_unlock(&ttm_bo_glob.lru_lock);
ttm_resource_manager_debug(man, &p);
return 0;
}
@ -1142,7 +1109,10 @@ static int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev,
if (ret)
return ret;
ret = ttm_bo_init_mm(&vmm->bdev, TTM_PL_VRAM, vram_size >> PAGE_SHIFT);
ret = ttm_range_man_init(&vmm->bdev, TTM_PL_VRAM,
TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC,
TTM_PL_FLAG_WC, false,
vram_size >> PAGE_SHIFT);
if (ret)
return ret;
@ -1151,6 +1121,7 @@ static int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev,
static void drm_vram_mm_cleanup(struct drm_vram_mm *vmm)
{
ttm_range_man_fini(&vmm->bdev, TTM_PL_VRAM);
ttm_bo_device_release(&vmm->bdev);
}

View file

@ -70,16 +70,12 @@ EXPORT_SYMBOL(drm_panel_init);
*
* Add a panel to the global registry so that it can be looked up by display
* drivers.
*
* Return: 0 on success or a negative error code on failure.
*/
int drm_panel_add(struct drm_panel *panel)
void drm_panel_add(struct drm_panel *panel)
{
mutex_lock(&panel_lock);
list_add_tail(&panel->list, &panel_list);
mutex_unlock(&panel_lock);
return 0;
}
EXPORT_SYMBOL(drm_panel_add);
@ -97,42 +93,6 @@ void drm_panel_remove(struct drm_panel *panel)
}
EXPORT_SYMBOL(drm_panel_remove);
/**
* drm_panel_attach - attach a panel to a connector
* @panel: DRM panel
* @connector: DRM connector
*
* After obtaining a pointer to a DRM panel a display driver calls this
* function to attach a panel to a connector.
*
* An error is returned if the panel is already attached to another connector.
*
* When unloading, the driver should detach from the panel by calling
* drm_panel_detach().
*
* Return: 0 on success or a negative error code on failure.
*/
int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector)
{
return 0;
}
EXPORT_SYMBOL(drm_panel_attach);
/**
* drm_panel_detach - detach a panel from a connector
* @panel: DRM panel
*
* Detaches a panel from the connector it is attached to. If a panel is not
* attached to any connector this is effectively a no-op.
*
* This function should not be called by the panel device itself. It
* is only for the drm device that called drm_panel_attach().
*/
void drm_panel_detach(struct drm_panel *panel)
{
}
EXPORT_SYMBOL(drm_panel_detach);
/**
* drm_panel_prepare - power on a panel
* @panel: DRM panel
@ -300,6 +260,49 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
return ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL(of_drm_find_panel);
/**
* of_drm_get_panel_orientation - look up the orientation of the panel through
* the "rotation" binding from a device tree node
* @np: device tree node of the panel
* @orientation: orientation enum to be filled in
*
* Looks up the rotation of a panel in the device tree. The orientation of the
* panel is expressed as a property name "rotation" in the device tree. The
* rotation in the device tree is counter clockwise.
*
* Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the
* rotation property doesn't exist. Return a negative error code on failure.
*/
int of_drm_get_panel_orientation(const struct device_node *np,
enum drm_panel_orientation *orientation)
{
int rotation, ret;
ret = of_property_read_u32(np, "rotation", &rotation);
if (ret == -EINVAL) {
/* Don't return an error if there's no rotation property. */
*orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
return 0;
}
if (ret < 0)
return ret;
if (rotation == 0)
*orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
else if (rotation == 90)
*orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
else if (rotation == 180)
*orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
else if (rotation == 270)
*orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
else
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(of_drm_get_panel_orientation);
#endif
#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)

View file

@ -297,7 +297,7 @@ void drm_syncobj_add_point(struct drm_syncobj *syncobj,
prev = drm_syncobj_fence_get(syncobj);
/* You are adding an unorder point to timeline, which could cause payload returned from query_ioctl is 0! */
if (prev && prev->seqno >= point)
DRM_ERROR("You are adding an unorder point to timeline!\n");
DRM_DEBUG("You are adding an unorder point to timeline!\n");
dma_fence_chain_init(chain, prev, fence, point);
rcu_assign_pointer(syncobj->fence, &chain->base);

View file

@ -42,11 +42,6 @@ static inline struct exynos_dpi *encoder_to_dpi(struct drm_encoder *e)
static enum drm_connector_status
exynos_dpi_detect(struct drm_connector *connector, bool force)
{
struct exynos_dpi *ctx = connector_to_dpi(connector);
if (ctx->panel)
drm_panel_attach(ctx->panel, &ctx->connector);
return connector_status_connected;
}
@ -249,8 +244,5 @@ int exynos_dpi_remove(struct drm_encoder *encoder)
exynos_dpi_disable(&ctx->encoder);
if (ctx->panel)
drm_panel_detach(ctx->panel);
return 0;
}

View file

@ -1551,12 +1551,10 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
}
dsi->panel = of_drm_find_panel(device->dev.of_node);
if (IS_ERR(dsi->panel)) {
if (IS_ERR(dsi->panel))
dsi->panel = NULL;
} else {
drm_panel_attach(dsi->panel, &dsi->connector);
else
dsi->connector.status = connector_status_connected;
}
}
/*
@ -1596,7 +1594,6 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
if (dsi->panel) {
mutex_lock(&drm->mode_config.mutex);
exynos_dsi_disable(&dsi->encoder);
drm_panel_detach(dsi->panel);
dsi->panel = NULL;
dsi->connector.status = connector_status_disconnected;
mutex_unlock(&drm->mode_config.mutex);

View file

@ -40,10 +40,7 @@ int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev,
static void fsl_dcu_drm_connector_destroy(struct drm_connector *connector)
{
struct fsl_dcu_drm_connector *fsl_con = to_fsl_dcu_connector(connector);
drm_connector_unregister(connector);
drm_panel_detach(fsl_con->panel);
drm_connector_cleanup(connector);
}
@ -101,12 +98,6 @@ static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev,
if (ret < 0)
goto err_sysfs;
ret = drm_panel_attach(panel, connector);
if (ret) {
dev_err(fsl_dev->dev, "failed to attach panel\n");
goto err_sysfs;
}
return 0;
err_sysfs:

View file

@ -2078,7 +2078,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
intel_dp->dpcd,
sizeof(intel_dp->dpcd));
cdv_intel_edp_panel_vdd_off(gma_encoder);
if (ret == 0) {
if (ret <= 0) {
/* if this fails, presume the device is a ghost */
DRM_INFO("failed to retrieve link info, disabling eDP\n");
drm_encoder_cleanup(encoder);

View file

@ -6,6 +6,7 @@
**************************************************************************/
#include <linux/delay.h>
#include <linux/gpio/machine.h>
#include <asm/intel_scu_ipc.h>
@ -505,12 +506,31 @@ static const struct psb_offset mdfld_regmap[3] = {
},
};
/*
* The GPIO lines for resetting DSI pipe 0 and 2 are available in the
* PCI device 0000:00:0c.0 on the Medfield.
*/
static struct gpiod_lookup_table mdfld_dsi_pipe_gpio_table = {
.table = {
GPIO_LOOKUP("0000:00:0c.0", 128, "dsi-pipe0-reset",
GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("0000:00:0c.0", 34, "dsi-pipe2-reset",
GPIO_ACTIVE_HIGH),
{ },
},
};
static int mdfld_chip_setup(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
if (pci_enable_msi(dev->pdev))
dev_warn(dev->dev, "Enabling MSI failed!\n");
dev_priv->regmap = mdfld_regmap;
/* Associate the GPIO lines with the DRM device */
mdfld_dsi_pipe_gpio_table.dev_id = dev_name(dev->dev);
gpiod_add_lookup_table(&mdfld_dsi_pipe_gpio_table);
return mid_chip_setup(dev);
}

View file

@ -955,7 +955,7 @@ struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev,
/* panel hard-reset */
if (p_funcs->reset) {
ret = p_funcs->reset(pipe);
ret = p_funcs->reset(dev, pipe);
if (ret) {
DRM_ERROR("Panel %d hard-reset failed\n", pipe);
return NULL;

View file

@ -28,6 +28,7 @@
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
#include <asm/intel_scu_ipc.h>
@ -366,7 +367,7 @@ static enum drm_mode_status mdfld_dsi_connector_mode_valid(struct drm_connector
/**
* FIXME: current DC has no fitting unit, reject any mode setting
* request
* Will figure out a way to do up-scaling(pannel fitting) later.
* Will figure out a way to do up-scaling(panel fitting) later.
**/
if (fixed_mode) {
if (mode->hdisplay != fixed_mode->hdisplay)
@ -432,42 +433,42 @@ static int mdfld_dsi_get_default_config(struct drm_device *dev,
return 0;
}
int mdfld_dsi_panel_reset(int pipe)
int mdfld_dsi_panel_reset(struct drm_device *ddev, int pipe)
{
unsigned gpio;
int ret = 0;
struct device *dev = ddev->dev;
struct gpio_desc *gpiod;
/*
* Raise the GPIO reset line for the corresponding pipe to HIGH,
* this is probably because it is active low so this takes the
* respective pipe out of reset. (We have no code to put it back
* into reset in this driver.)
*/
switch (pipe) {
case 0:
gpio = 128;
gpiod = gpiod_get(dev, "dsi-pipe0-reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpiod))
return PTR_ERR(gpiod);
break;
case 2:
gpio = 34;
gpiod = gpiod_get(dev, "dsi-pipe2-reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpiod))
return PTR_ERR(gpiod);
break;
default:
DRM_ERROR("Invalid output\n");
DRM_DEV_ERROR(dev, "Invalid output pipe\n");
return -EINVAL;
}
gpiod_put(gpiod);
ret = gpio_request(gpio, "gfx");
if (ret) {
DRM_ERROR("gpio_rqueset failed\n");
return ret;
}
/* Flush posted writes on the device */
gpiod = gpiod_get(dev, "dsi-pipe0-reset", GPIOD_ASIS);
if (IS_ERR(gpiod))
return PTR_ERR(gpiod);
gpiod_get_value(gpiod);
gpiod_put(gpiod);
ret = gpio_direction_output(gpio, 1);
if (ret) {
DRM_ERROR("gpio_direction_output failed\n");
goto gpio_error;
}
gpio_get_value(128);
gpio_error:
if (gpio_is_valid(gpio))
gpio_free(gpio);
return ret;
return 0;
}
/*
@ -531,7 +532,7 @@ void mdfld_dsi_output_init(struct drm_device *dev,
dsi_config->connector = dsi_connector;
if (!dsi_config->fixed_mode) {
DRM_ERROR("No pannel fixed mode was found\n");
DRM_ERROR("No panel fixed mode was found\n");
goto dsi_init_err0;
}

View file

@ -372,6 +372,6 @@ extern void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config,
extern int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config,
u32 *mode, bool hs);
extern int mdfld_dsi_panel_reset(int pipe);
extern int mdfld_dsi_panel_reset(struct drm_device *dev, int pipe);
#endif /*__MDFLD_DSI_OUTPUT_H__*/

View file

@ -54,7 +54,7 @@ struct panel_funcs {
const struct drm_encoder_helper_funcs *encoder_helper_funcs;
struct drm_display_mode * (*get_config_mode)(struct drm_device *);
int (*get_panel_info)(struct drm_device *, int, struct panel_info *);
int (*reset)(int pipe);
int (*reset)(struct drm_device *, int);
void (*drv_ic_init)(struct mdfld_dsi_config *dsi_config, int pipe);
};

View file

@ -13,7 +13,6 @@
#include <drm/drm_encoder.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include <linux/gpio.h>
#include "gma_display.h"
/*

View file

@ -125,7 +125,7 @@ struct psb_intel_sdvo {
bool is_lvds;
/**
* This is sdvo fixed pannel mode pointer
* This is sdvo fixed panel mode pointer
*/
struct drm_display_mode *sdvo_lvds_fixed_mode;

View file

@ -17,9 +17,6 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_vram_helper.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include "hibmc_drm_drv.h"
@ -74,12 +71,12 @@ static int hibmc_plane_atomic_check(struct drm_plane *plane,
return PTR_ERR(crtc_state);
if (src_w != state->crtc_w || src_h != state->crtc_h) {
DRM_DEBUG_ATOMIC("scale not support\n");
drm_dbg_atomic(plane->dev, "scale not support\n");
return -EINVAL;
}
if (state->crtc_x < 0 || state->crtc_y < 0) {
DRM_DEBUG_ATOMIC("crtc_x/y of drm_plane state is invalid\n");
drm_dbg_atomic(plane->dev, "crtc_x/y of drm_plane state is invalid\n");
return -EINVAL;
}
@ -90,12 +87,12 @@ static int hibmc_plane_atomic_check(struct drm_plane *plane,
crtc_state->adjusted_mode.hdisplay ||
state->crtc_y + state->crtc_h >
crtc_state->adjusted_mode.vdisplay) {
DRM_DEBUG_ATOMIC("visible portion of plane is invalid\n");
drm_dbg_atomic(plane->dev, "visible portion of plane is invalid\n");
return -EINVAL;
}
if (state->fb->pitches[0] % 128 != 0) {
DRM_DEBUG_ATOMIC("wrong stride with 128-byte aligned\n");
drm_dbg_atomic(plane->dev, "wrong stride with 128-byte aligned\n");
return -EINVAL;
}
return 0;
@ -160,37 +157,6 @@ static const struct drm_plane_helper_funcs hibmc_plane_helper_funcs = {
.atomic_update = hibmc_plane_atomic_update,
};
static struct drm_plane *hibmc_plane_init(struct hibmc_drm_private *priv)
{
struct drm_device *dev = priv->dev;
struct drm_plane *plane;
int ret = 0;
plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
if (!plane) {
DRM_ERROR("failed to alloc memory when init plane\n");
return ERR_PTR(-ENOMEM);
}
/*
* plane init
* TODO: Now only support primary plane, overlay planes
* need to do.
*/
ret = drm_universal_plane_init(dev, plane, 1, &hibmc_plane_funcs,
channel_formats1,
ARRAY_SIZE(channel_formats1),
NULL,
DRM_PLANE_TYPE_PRIMARY,
NULL);
if (ret) {
DRM_ERROR("failed to init plane: %d\n", ret);
return ERR_PTR(ret);
}
drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
return plane;
}
static void hibmc_crtc_dpms(struct drm_crtc *crtc, int dpms)
{
struct hibmc_drm_private *priv = crtc->dev->dev_private;
@ -537,32 +503,34 @@ static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = {
int hibmc_de_init(struct hibmc_drm_private *priv)
{
struct drm_device *dev = priv->dev;
struct drm_crtc *crtc;
struct drm_plane *plane;
struct drm_crtc *crtc = &priv->crtc;
struct drm_plane *plane = &priv->primary_plane;
int ret;
plane = hibmc_plane_init(priv);
if (IS_ERR(plane)) {
DRM_ERROR("failed to create plane: %ld\n", PTR_ERR(plane));
return PTR_ERR(plane);
ret = drm_universal_plane_init(dev, plane, 1, &hibmc_plane_funcs,
channel_formats1,
ARRAY_SIZE(channel_formats1),
NULL,
DRM_PLANE_TYPE_PRIMARY,
NULL);
if (ret) {
drm_err(dev, "failed to init plane: %d\n", ret);
return ret;
}
crtc = devm_kzalloc(dev->dev, sizeof(*crtc), GFP_KERNEL);
if (!crtc) {
DRM_ERROR("failed to alloc memory when init crtc\n");
return -ENOMEM;
}
drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
ret = drm_crtc_init_with_planes(dev, crtc, plane,
NULL, &hibmc_crtc_funcs, NULL);
if (ret) {
DRM_ERROR("failed to init crtc: %d\n", ret);
drm_err(dev, "failed to init crtc: %d\n", ret);
return ret;
}
ret = drm_mode_crtc_set_gamma_size(crtc, 256);
if (ret) {
DRM_ERROR("failed to set gamma size: %d\n", ret);
drm_err(dev, "failed to set gamma size: %d\n", ret);
return ret;
}
drm_crtc_helper_add(crtc, &hibmc_crtc_helper_funcs);

View file

@ -11,18 +11,14 @@
* Jianhua Li <lijianhua@huawei.com>
*/
#include <linux/console.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_vram_helper.h>
#include <drm/drm_irq.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include "hibmc_drm_drv.h"
@ -102,13 +98,13 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv)
ret = hibmc_de_init(priv);
if (ret) {
DRM_ERROR("failed to init de: %d\n", ret);
drm_err(priv->dev, "failed to init de: %d\n", ret);
return ret;
}
ret = hibmc_vdac_init(priv);
if (ret) {
DRM_ERROR("failed to init vdac: %d\n", ret);
drm_err(priv->dev, "failed to init vdac: %d\n", ret);
return ret;
}
@ -216,7 +212,7 @@ static int hibmc_hw_map(struct hibmc_drm_private *priv)
iosize = pci_resource_len(pdev, 1);
priv->mmio = devm_ioremap(dev->dev, ioaddr, iosize);
if (!priv->mmio) {
DRM_ERROR("Cannot map mmio region\n");
drm_err(dev, "Cannot map mmio region\n");
return -ENOMEM;
}
@ -224,7 +220,7 @@ static int hibmc_hw_map(struct hibmc_drm_private *priv)
size = pci_resource_len(pdev, 0);
priv->fb_map = devm_ioremap(dev->dev, addr, size);
if (!priv->fb_map) {
DRM_ERROR("Cannot map framebuffer\n");
drm_err(dev, "Cannot map framebuffer\n");
return -ENOMEM;
}
priv->fb_base = addr;
@ -254,9 +250,8 @@ static int hibmc_unload(struct drm_device *dev)
if (dev->irq_enabled)
drm_irq_uninstall(dev);
if (priv->msi_enabled)
pci_disable_msi(dev->pdev);
pci_disable_msi(dev->pdev);
hibmc_kms_fini(priv);
hibmc_mm_fini(priv);
dev->dev_private = NULL;
@ -270,7 +265,7 @@ static int hibmc_load(struct drm_device *dev)
priv = drmm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
DRM_ERROR("no memory to allocate for hibmc_drm_private\n");
drm_err(dev, "no memory to allocate for hibmc_drm_private\n");
return -ENOMEM;
}
dev->dev_private = priv;
@ -290,19 +285,17 @@ static int hibmc_load(struct drm_device *dev)
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret) {
DRM_ERROR("failed to initialize vblank: %d\n", ret);
drm_err(dev, "failed to initialize vblank: %d\n", ret);
goto err;
}
priv->msi_enabled = 0;
ret = pci_enable_msi(dev->pdev);
if (ret) {
DRM_WARN("enabling MSI failed: %d\n", ret);
drm_warn(dev, "enabling MSI failed: %d\n", ret);
} else {
priv->msi_enabled = 1;
ret = drm_irq_install(dev, dev->pdev->irq);
if (ret)
DRM_WARN("install irq failed: %d\n", ret);
drm_warn(dev, "install irq failed: %d\n", ret);
}
/* reset all the states of crtc/plane/encoder/connector */
@ -312,7 +305,7 @@ static int hibmc_load(struct drm_device *dev)
err:
hibmc_unload(dev);
DRM_ERROR("failed to initialize drm driver: %d\n", ret);
drm_err(dev, "failed to initialize drm driver: %d\n", ret);
return ret;
}
@ -338,19 +331,19 @@ static int hibmc_pci_probe(struct pci_dev *pdev,
ret = pci_enable_device(pdev);
if (ret) {
DRM_ERROR("failed to enable pci device: %d\n", ret);
drm_err(dev, "failed to enable pci device: %d\n", ret);
goto err_free;
}
ret = hibmc_load(dev);
if (ret) {
DRM_ERROR("failed to load hibmc: %d\n", ret);
drm_err(dev, "failed to load hibmc: %d\n", ret);
goto err_disable;
}
ret = drm_dev_register(dev, 0);
if (ret) {
DRM_ERROR("failed to register drv for userspace access: %d\n",
drm_err(dev, "failed to register drv for userspace access: %d\n",
ret);
goto err_unload;
}

View file

@ -25,10 +25,11 @@ struct hibmc_drm_private {
void __iomem *fb_map;
unsigned long fb_base;
unsigned long fb_size;
bool msi_enabled;
/* drm */
struct drm_device *dev;
struct drm_plane primary_plane;
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector connector;
bool mode_config_initialized;

View file

@ -11,10 +11,8 @@
* Jianhua Li <lijianhua@huawei.com>
*/
#include <drm/drm_gem_vram_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_print.h>
#include "hibmc_drm_drv.h"
@ -87,7 +85,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv)
ret = drm_encoder_init(dev, encoder, &hibmc_encoder_funcs,
DRM_MODE_ENCODER_DAC, NULL);
if (ret) {
DRM_ERROR("failed to init encoder: %d\n", ret);
drm_err(dev, "failed to init encoder: %d\n", ret);
return ret;
}
@ -96,7 +94,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv)
ret = drm_connector_init(dev, connector, &hibmc_connector_funcs,
DRM_MODE_CONNECTOR_VGA);
if (ret) {
DRM_ERROR("failed to init connector: %d\n", ret);
drm_err(dev, "failed to init connector: %d\n", ret);
return ret;
}
drm_connector_helper_add(connector, &hibmc_connector_helper_funcs);

View file

@ -32,7 +32,7 @@ int hibmc_mm_init(struct hibmc_drm_private *hibmc)
hibmc->fb_size);
if (IS_ERR(vmm)) {
ret = PTR_ERR(vmm);
DRM_ERROR("Error initializing VRAM MM; %d\n", ret);
drm_err(dev, "Error initializing VRAM MM; %d\n", ret);
return ret;
}

View file

@ -6243,6 +6243,11 @@ intel_dp_detect(struct drm_connector *connector,
*/
intel_display_power_flush_work(dev_priv);
if (!intel_dp_is_edp(intel_dp))
drm_dp_set_subconnector_property(connector,
status,
intel_dp->dpcd,
intel_dp->downstream_ports);
return status;
}
@ -7312,6 +7317,9 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
struct drm_i915_private *dev_priv = to_i915(connector->dev);
enum port port = dp_to_dig_port(intel_dp)->base.port;
if (!intel_dp_is_edp(intel_dp))
drm_connector_attach_dp_subconnector_property(connector);
if (!IS_G4X(dev_priv) && port != PORT_A)
intel_attach_force_audio_property(connector);

View file

@ -455,13 +455,6 @@ static int imx_ldb_register(struct drm_device *drm,
drm_connector_attach_encoder(&imx_ldb_ch->connector, encoder);
}
if (imx_ldb_ch->panel) {
ret = drm_panel_attach(imx_ldb_ch->panel,
&imx_ldb_ch->connector);
if (ret)
return ret;
}
return 0;
}
@ -702,9 +695,6 @@ static void imx_ldb_unbind(struct device *dev, struct device *master,
for (i = 0; i < 2; i++) {
struct imx_ldb_channel *channel = &imx_ldb->channel[i];
if (channel->panel)
drm_panel_detach(channel->panel);
kfree(channel->edid);
i2c_put_adapter(channel->ddc);
}

View file

@ -289,9 +289,6 @@ static int imx_pd_register(struct drm_device *drm,
DRM_MODE_CONNECTOR_DPI);
}
if (imxpd->panel)
drm_panel_attach(imxpd->panel, &imxpd->connector);
if (imxpd->next_bridge) {
ret = drm_bridge_attach(encoder, imxpd->next_bridge,
&imxpd->bridge, 0);
@ -357,9 +354,6 @@ static void imx_pd_unbind(struct device *dev, struct device *master,
{
struct imx_parallel_display *imxpd = dev_get_drvdata(dev);
if (imxpd->panel)
drm_panel_detach(imxpd->panel);
kfree(imxpd->edid);
}

View file

@ -199,26 +199,20 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
{
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL;
long rate;
if (!drm_atomic_crtc_needs_modeset(state))
return 0;
if (state->mode.hdisplay > priv->soc_info->max_width ||
state->mode.vdisplay > priv->soc_info->max_height)
return -EINVAL;
rate = clk_round_rate(priv->pix_clk,
state->adjusted_mode.clock * 1000);
if (rate < 0)
return rate;
if (priv->soc_info->has_osd) {
if (drm_atomic_crtc_needs_modeset(state) && priv->soc_info->has_osd) {
f1_state = drm_atomic_get_plane_state(state->state, &priv->f1);
if (IS_ERR(f1_state))
return PTR_ERR(f1_state);
f0_state = drm_atomic_get_plane_state(state->state, &priv->f0);
if (IS_ERR(f0_state))
return PTR_ERR(f0_state);
if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && priv->ipu_plane) {
ipu_state = drm_atomic_get_plane_state(state->state, priv->ipu_plane);
if (IS_ERR(ipu_state))
return PTR_ERR(ipu_state);
/* IPU and F1 planes cannot be enabled at the same time. */
if (f1_state->fb && ipu_state->fb) {
@ -235,6 +229,24 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
return 0;
}
static enum drm_mode_status
ingenic_drm_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
{
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
long rate;
if (mode->hdisplay > priv->soc_info->max_width)
return MODE_BAD_HVALUE;
if (mode->vdisplay > priv->soc_info->max_height)
return MODE_BAD_VVALUE;
rate = clk_round_rate(priv->pix_clk, mode->clock * 1000);
if (rate < 0)
return MODE_CLOCK_RANGE;
return MODE_OK;
}
static void ingenic_drm_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *oldstate)
{
@ -648,6 +660,7 @@ static const struct drm_crtc_helper_funcs ingenic_drm_crtc_helper_funcs = {
.atomic_begin = ingenic_drm_crtc_atomic_begin,
.atomic_flush = ingenic_drm_crtc_atomic_flush,
.atomic_check = ingenic_drm_crtc_atomic_check,
.mode_valid = ingenic_drm_crtc_mode_valid,
};
static const struct drm_encoder_helper_funcs ingenic_drm_encoder_helper_funcs = {

View file

@ -35,6 +35,7 @@ struct soc_info {
const u32 *formats;
size_t num_formats;
bool has_bicubic;
bool manual_restart;
void (*set_coefs)(struct ingenic_ipu *ipu, unsigned int reg,
unsigned int sharpness, bool downscale,
@ -48,6 +49,7 @@ struct ingenic_ipu {
struct regmap *map;
struct clk *clk;
const struct soc_info *soc_info;
bool clk_enabled;
unsigned int num_w, num_h, denom_w, denom_h;
@ -287,12 +289,23 @@ static void ingenic_ipu_plane_atomic_update(struct drm_plane *plane,
const struct drm_format_info *finfo;
u32 ctrl, stride = 0, coef_index = 0, format = 0;
bool needs_modeset, upscaling_w, upscaling_h;
int err;
if (!state || !state->fb)
return;
finfo = drm_format_info(state->fb->format->format);
if (!ipu->clk_enabled) {
err = clk_enable(ipu->clk);
if (err) {
dev_err(ipu->dev, "Unable to enable clock: %d\n", err);
return;
}
ipu->clk_enabled = true;
}
/* Reset all the registers if needed */
needs_modeset = drm_atomic_crtc_needs_modeset(state->crtc->state);
if (needs_modeset) {
@ -577,6 +590,11 @@ static void ingenic_ipu_plane_atomic_disable(struct drm_plane *plane,
regmap_clear_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CHIP_EN);
ingenic_drm_plane_disable(ipu->master, plane);
if (ipu->clk_enabled) {
clk_disable(ipu->clk);
ipu->clk_enabled = false;
}
}
static const struct drm_plane_helper_funcs ingenic_ipu_plane_helper_funcs = {
@ -645,7 +663,8 @@ static irqreturn_t ingenic_ipu_irq_handler(int irq, void *arg)
unsigned int dummy;
/* dummy read allows CPU to reconfigure IPU */
regmap_read(ipu->map, JZ_REG_IPU_STATUS, &dummy);
if (ipu->soc_info->manual_restart)
regmap_read(ipu->map, JZ_REG_IPU_STATUS, &dummy);
/* ACK interrupt */
regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0);
@ -656,7 +675,8 @@ static irqreturn_t ingenic_ipu_irq_handler(int irq, void *arg)
regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v);
/* Run IPU for the new frame */
regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RUN);
if (ipu->soc_info->manual_restart)
regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RUN);
drm_crtc_handle_vblank(crtc);
@ -758,9 +778,9 @@ static int ingenic_ipu_bind(struct device *dev, struct device *master, void *d)
drm_object_attach_property(&plane->base, ipu->sharpness_prop,
ipu->sharpness);
err = clk_prepare_enable(ipu->clk);
err = clk_prepare(ipu->clk);
if (err) {
dev_err(dev, "Unable to enable clock\n");
dev_err(dev, "Unable to prepare clock\n");
return err;
}
@ -772,7 +792,7 @@ static void ingenic_ipu_unbind(struct device *dev,
{
struct ingenic_ipu *ipu = dev_get_drvdata(dev);
clk_disable_unprepare(ipu->clk);
clk_unprepare(ipu->clk);
}
static const struct component_ops ingenic_ipu_ops = {
@ -792,10 +812,16 @@ static int ingenic_ipu_remove(struct platform_device *pdev)
}
static const u32 jz4725b_ipu_formats[] = {
/*
* While officially supported, packed YUV 4:2:2 formats can cause
* random hardware crashes on JZ4725B under certain circumstances.
* It seems to happen with some specific resize ratios.
* Until a proper workaround or fix is found, disable these formats.
DRM_FORMAT_YUYV,
DRM_FORMAT_YVYU,
DRM_FORMAT_UYVY,
DRM_FORMAT_VYUY,
*/
DRM_FORMAT_YUV411,
DRM_FORMAT_YUV420,
DRM_FORMAT_YUV422,
@ -806,6 +832,7 @@ static const struct soc_info jz4725b_soc_info = {
.formats = jz4725b_ipu_formats,
.num_formats = ARRAY_SIZE(jz4725b_ipu_formats),
.has_bicubic = false,
.manual_restart = true,
.set_coefs = jz4725b_set_coefs,
};
@ -831,6 +858,7 @@ static const struct soc_info jz4760_soc_info = {
.formats = jz4760_ipu_formats,
.num_formats = ARRAY_SIZE(jz4760_ipu_formats),
.has_bicubic = true,
.manual_restart = false,
.set_coefs = jz4760_set_coefs,
};

View file

@ -7,6 +7,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-buf.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_device.h>
#include <drm/drm_fb_cma_helper.h>
@ -89,7 +90,7 @@ void mcde_display_irq(struct mcde *mcde)
* the update function is called, then we disable the
* flow on the channel once we get the TE IRQ.
*/
if (mcde->oneshot_mode) {
if (mcde->flow_mode == MCDE_COMMAND_ONESHOT_FLOW) {
spin_lock(&mcde->flow_lock);
if (--mcde->flow_active == 0) {
dev_dbg(mcde->dev, "TE0 IRQ\n");
@ -333,7 +334,7 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
enum mcde_extsrc src,
enum mcde_channel ch,
const struct drm_display_mode *mode,
u32 format)
u32 format, int cpp)
{
u32 val;
u32 conf1;
@ -342,6 +343,7 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
u32 ljinc;
u32 cr;
u32 comp;
u32 pixel_fetcher_watermark;
switch (ovl) {
case MCDE_OVERLAY_0:
@ -426,8 +428,33 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
format);
break;
}
/* The default watermark level for overlay 0 is 48 */
val |= 48 << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT;
/*
* Pixel fetch watermark level is max 0x1FFF pixels.
* Two basic rules should be followed:
* 1. The value should be at least 256 bits.
* 2. The sum of all active overlays pixelfetch watermark level
* multiplied with bits per pixel, should be lower than the
* size of input_fifo_size in bits.
* 3. The value should be a multiple of a line (256 bits).
*/
switch (cpp) {
case 2:
pixel_fetcher_watermark = 128;
break;
case 3:
pixel_fetcher_watermark = 96;
break;
case 4:
pixel_fetcher_watermark = 48;
break;
default:
pixel_fetcher_watermark = 48;
break;
}
dev_dbg(mcde->dev, "pixel fetcher watermark level %d pixels\n",
pixel_fetcher_watermark);
val |= pixel_fetcher_watermark << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT;
writel(val, mcde->regs + conf2);
/* Number of bytes to fetch per line */
@ -498,19 +525,47 @@ static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch,
}
/* Set up channel 0 sync (based on chnl_update_registers()) */
if (mcde->video_mode || mcde->te_sync)
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
else
switch (mcde->flow_mode) {
case MCDE_COMMAND_ONESHOT_FLOW:
/* Oneshot is achieved with software sync */
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
if (mcde->te_sync)
break;
case MCDE_COMMAND_TE_FLOW:
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0
<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
else
break;
case MCDE_COMMAND_BTA_TE_FLOW:
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
/*
* TODO:
* The vendor driver uses the formatter as sync source
* for BTA TE mode. Test to use TE if you have a panel
* that uses this mode.
*/
val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER
<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
break;
case MCDE_VIDEO_TE_FLOW:
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0
<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
break;
case MCDE_VIDEO_FORMATTER_FLOW:
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER
<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
break;
default:
dev_err(mcde->dev, "unknown flow mode %d\n",
mcde->flow_mode);
break;
}
writel(val, mcde->regs + sync);
@ -825,6 +880,14 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
u32 formatter_frame;
u32 pkt_div;
u32 val;
int ret;
/* This powers up the entire MCDE block and the DSI hardware */
ret = regulator_enable(mcde->epod);
if (ret) {
dev_err(drm->dev, "can't re-enable EPOD regulator\n");
return;
}
dev_info(drm->dev, "enable MCDE, %d x %d format %s\n",
mode->hdisplay, mode->vdisplay,
@ -835,6 +898,26 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
return;
}
/* Set up the main control, watermark level at 7 */
val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT;
/* 24 bits DPI: connect LSB Ch B to D[0:7] */
val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT;
/* TV out: connect LSB Ch B to D[8:15] */
val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT;
/* Don't care about this muxing */
val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT;
/* 24 bits DPI: connect MID Ch B to D[24:31] */
val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT;
/* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */
val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT;
/* Syncmux bits zero: DPI channel A and B on output pins A and B resp */
writel(val, mcde->regs + MCDE_CONF0);
/* Clear any pending interrupts */
mcde_display_disable_irqs(mcde);
writel(0, mcde->regs + MCDE_IMSCERR);
writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR);
dev_info(drm->dev, "output in %s mode, format %dbpp\n",
(mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ?
"VIDEO" : "CMD",
@ -904,7 +987,7 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
* channel 0
*/
mcde_configure_overlay(mcde, MCDE_OVERLAY_0, MCDE_EXTSRC_0,
MCDE_CHANNEL_0, mode, format);
MCDE_CHANNEL_0, mode, format, cpp);
/*
* Configure pixel-per-line and line-per-frame for channel 0 and then
@ -916,11 +999,25 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0,
fifo_wtrmrk);
/*
* This brings up the DSI bridge which is tightly connected
* to the MCDE DSI formatter.
*
* FIXME: if we want to use another formatter, such as DPI,
* we need to be more elaborate here and select the appropriate
* bridge.
*/
mcde_dsi_enable(mcde->bridge);
/* Configure the DSI formatter 0 for the DSI panel output */
mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0,
formatter_frame, pkt_size);
if (mcde->te_sync) {
switch (mcde->flow_mode) {
case MCDE_COMMAND_TE_FLOW:
case MCDE_COMMAND_BTA_TE_FLOW:
case MCDE_VIDEO_TE_FLOW:
/* We are using TE in some comination */
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
val = MCDE_VSCRC_VSPOL;
else
@ -930,16 +1027,31 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
val = readl(mcde->regs + MCDE_CRC);
val |= MCDE_CRC_SYCEN0;
writel(val, mcde->regs + MCDE_CRC);
break;
default:
/* No TE capture */
break;
}
drm_crtc_vblank_on(crtc);
if (mcde->video_mode)
/*
* Keep FIFO permanently enabled in video mode,
* otherwise MCDE will stop feeding data to the panel.
*/
/*
* If we're using oneshot mode we don't start the flow
* until each time the display is given an update, and
* then we disable it immediately after. For all other
* modes (command or video) we start the FIFO flow
* right here. This is necessary for the hardware to
* behave right.
*/
if (mcde->flow_mode != MCDE_COMMAND_ONESHOT_FLOW) {
mcde_enable_fifo(mcde, MCDE_FIFO_A);
dev_dbg(mcde->dev, "started MCDE video FIFO flow\n");
}
/* Enable MCDE with automatic clock gating */
val = readl(mcde->regs + MCDE_CR);
val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN;
writel(val, mcde->regs + MCDE_CR);
dev_info(drm->dev, "MCDE display is enabled\n");
}
@ -950,12 +1062,16 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
struct drm_device *drm = crtc->dev;
struct mcde *mcde = to_mcde(drm);
struct drm_pending_vblank_event *event;
int ret;
drm_crtc_vblank_off(crtc);
/* Disable FIFO A flow */
mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
/* This disables the DSI bridge */
mcde_dsi_disable(mcde->bridge);
event = crtc->state->event;
if (event) {
crtc->state->event = NULL;
@ -965,43 +1081,47 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
spin_unlock_irq(&crtc->dev->event_lock);
}
ret = regulator_disable(mcde->epod);
if (ret)
dev_err(drm->dev, "can't disable EPOD regulator\n");
/* Make sure we are powered down (before we may power up again) */
usleep_range(50000, 70000);
dev_info(drm->dev, "MCDE display is disabled\n");
}
static void mcde_display_send_one_frame(struct mcde *mcde)
static void mcde_start_flow(struct mcde *mcde)
{
/* Request a TE ACK */
if (mcde->te_sync)
/* Request a TE ACK only in TE+BTA mode */
if (mcde->flow_mode == MCDE_COMMAND_BTA_TE_FLOW)
mcde_dsi_te_request(mcde->mdsi);
/* Enable FIFO A flow */
mcde_enable_fifo(mcde, MCDE_FIFO_A);
if (mcde->te_sync) {
/*
* If oneshot mode is enabled, the flow will be disabled
* when the TE0 IRQ arrives in the interrupt handler. Otherwise
* updates are continuously streamed to the display after this
* point.
*/
if (mcde->flow_mode == MCDE_COMMAND_ONESHOT_FLOW) {
/* Trigger a software sync out on channel 0 */
writel(MCDE_CHNLXSYNCHSW_SW_TRIG,
mcde->regs + MCDE_CHNL0SYNCHSW);
/*
* If oneshot mode is enabled, the flow will be disabled
* when the TE0 IRQ arrives in the interrupt handler. Otherwise
* updates are continuously streamed to the display after this
* point.
* Disable FIFO A flow again: since we are using TE sync we
* need to wait for the FIFO to drain before we continue
* so repeated calls to this function will not cause a mess
* in the hardware by pushing updates will updates are going
* on already.
*/
dev_dbg(mcde->dev, "sent TE0 framebuffer update\n");
return;
mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
}
/* Trigger a software sync out on channel 0 */
writel(MCDE_CHNLXSYNCHSW_SW_TRIG,
mcde->regs + MCDE_CHNL0SYNCHSW);
/*
* Disable FIFO A flow again: since we are using TE sync we
* need to wait for the FIFO to drain before we continue
* so repeated calls to this function will not cause a mess
* in the hardware by pushing updates will updates are going
* on already.
*/
mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
dev_dbg(mcde->dev, "sent SW framebuffer update\n");
dev_dbg(mcde->dev, "started MCDE FIFO flow\n");
}
static void mcde_set_extsrc(struct mcde *mcde, u32 buffer_address)
@ -1060,15 +1180,13 @@ static void mcde_display_update(struct drm_simple_display_pipe *pipe,
*/
if (fb) {
mcde_set_extsrc(mcde, drm_fb_cma_get_gem_addr(fb, pstate, 0));
if (!mcde->video_mode) {
/*
* Send a single frame using software sync if the flow
* is not active yet.
*/
if (mcde->flow_active == 0)
mcde_display_send_one_frame(mcde);
}
dev_info_once(mcde->dev, "sent first display update\n");
dev_info_once(mcde->dev, "first update of display contents\n");
/*
* Usually the flow is already active, unless we are in
* oneshot mode, then we need to kick the flow right here.
*/
if (mcde->flow_active == 0)
mcde_start_flow(mcde);
} else {
/*
* If an update is receieved before the MCDE is enabled

View file

@ -9,6 +9,61 @@
#ifndef _MCDE_DRM_H_
#define _MCDE_DRM_H_
/* Shared basic registers */
#define MCDE_CR 0x00000000
#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_SHIFT 0
#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_MASK 0x0000003F
#define MCDE_CR_IFIFOCTRLEN BIT(15)
#define MCDE_CR_UFRECOVERY_MODE_V422 BIT(16)
#define MCDE_CR_WRAP_MODE_V422_SHIFT BIT(17)
#define MCDE_CR_AUTOCLKG_EN BIT(30)
#define MCDE_CR_MCDEEN BIT(31)
#define MCDE_CONF0 0x00000004
#define MCDE_CONF0_SYNCMUX0 BIT(0)
#define MCDE_CONF0_SYNCMUX1 BIT(1)
#define MCDE_CONF0_SYNCMUX2 BIT(2)
#define MCDE_CONF0_SYNCMUX3 BIT(3)
#define MCDE_CONF0_SYNCMUX4 BIT(4)
#define MCDE_CONF0_SYNCMUX5 BIT(5)
#define MCDE_CONF0_SYNCMUX6 BIT(6)
#define MCDE_CONF0_SYNCMUX7 BIT(7)
#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT 12
#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_MASK 0x00007000
#define MCDE_CONF0_OUTMUX0_SHIFT 16
#define MCDE_CONF0_OUTMUX0_MASK 0x00070000
#define MCDE_CONF0_OUTMUX1_SHIFT 19
#define MCDE_CONF0_OUTMUX1_MASK 0x00380000
#define MCDE_CONF0_OUTMUX2_SHIFT 22
#define MCDE_CONF0_OUTMUX2_MASK 0x01C00000
#define MCDE_CONF0_OUTMUX3_SHIFT 25
#define MCDE_CONF0_OUTMUX3_MASK 0x0E000000
#define MCDE_CONF0_OUTMUX4_SHIFT 28
#define MCDE_CONF0_OUTMUX4_MASK 0x70000000
#define MCDE_SSP 0x00000008
#define MCDE_AIS 0x00000100
#define MCDE_IMSCERR 0x00000110
#define MCDE_RISERR 0x00000120
#define MCDE_MISERR 0x00000130
#define MCDE_SISERR 0x00000140
enum mcde_flow_mode {
/* One-shot mode: flow stops after one frame */
MCDE_COMMAND_ONESHOT_FLOW,
/* Command mode with tearing effect (TE) IRQ sync */
MCDE_COMMAND_TE_FLOW,
/*
* Command mode with bus turn-around (BTA) and tearing effect
* (TE) IRQ sync.
*/
MCDE_COMMAND_BTA_TE_FLOW,
/* Video mode with tearing effect (TE) sync IRQ */
MCDE_VIDEO_TE_FLOW,
/* Video mode with the formatter itself as sync source */
MCDE_VIDEO_FORMATTER_FLOW,
};
struct mcde {
struct drm_device drm;
struct device *dev;
@ -18,9 +73,7 @@ struct mcde {
struct drm_simple_display_pipe pipe;
struct mipi_dsi_device *mdsi;
s16 stride;
bool te_sync;
bool video_mode;
bool oneshot_mode;
enum mcde_flow_mode flow_mode;
unsigned int flow_active;
spinlock_t flow_lock; /* Locks the channel flow control */
@ -36,8 +89,16 @@ struct mcde {
#define to_mcde(dev) container_of(dev, struct mcde, drm)
static inline bool mcde_flow_is_video(struct mcde *mcde)
{
return (mcde->flow_mode == MCDE_VIDEO_TE_FLOW ||
mcde->flow_mode == MCDE_VIDEO_FORMATTER_FLOW);
}
bool mcde_dsi_irq(struct mipi_dsi_device *mdsi);
void mcde_dsi_te_request(struct mipi_dsi_device *mdsi);
void mcde_dsi_enable(struct drm_bridge *bridge);
void mcde_dsi_disable(struct drm_bridge *bridge);
extern struct platform_driver mcde_dsi_driver;
void mcde_display_irq(struct mcde *mcde);

View file

@ -63,6 +63,7 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
@ -82,44 +83,6 @@
#define DRIVER_DESC "DRM module for MCDE"
#define MCDE_CR 0x00000000
#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_SHIFT 0
#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_MASK 0x0000003F
#define MCDE_CR_IFIFOCTRLEN BIT(15)
#define MCDE_CR_UFRECOVERY_MODE_V422 BIT(16)
#define MCDE_CR_WRAP_MODE_V422_SHIFT BIT(17)
#define MCDE_CR_AUTOCLKG_EN BIT(30)
#define MCDE_CR_MCDEEN BIT(31)
#define MCDE_CONF0 0x00000004
#define MCDE_CONF0_SYNCMUX0 BIT(0)
#define MCDE_CONF0_SYNCMUX1 BIT(1)
#define MCDE_CONF0_SYNCMUX2 BIT(2)
#define MCDE_CONF0_SYNCMUX3 BIT(3)
#define MCDE_CONF0_SYNCMUX4 BIT(4)
#define MCDE_CONF0_SYNCMUX5 BIT(5)
#define MCDE_CONF0_SYNCMUX6 BIT(6)
#define MCDE_CONF0_SYNCMUX7 BIT(7)
#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT 12
#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_MASK 0x00007000
#define MCDE_CONF0_OUTMUX0_SHIFT 16
#define MCDE_CONF0_OUTMUX0_MASK 0x00070000
#define MCDE_CONF0_OUTMUX1_SHIFT 19
#define MCDE_CONF0_OUTMUX1_MASK 0x00380000
#define MCDE_CONF0_OUTMUX2_SHIFT 22
#define MCDE_CONF0_OUTMUX2_MASK 0x01C00000
#define MCDE_CONF0_OUTMUX3_SHIFT 25
#define MCDE_CONF0_OUTMUX3_MASK 0x0E000000
#define MCDE_CONF0_OUTMUX4_SHIFT 28
#define MCDE_CONF0_OUTMUX4_MASK 0x70000000
#define MCDE_SSP 0x00000008
#define MCDE_AIS 0x00000100
#define MCDE_IMSCERR 0x00000110
#define MCDE_RISERR 0x00000120
#define MCDE_MISERR 0x00000130
#define MCDE_SISERR 0x00000140
#define MCDE_PID 0x000001FC
#define MCDE_PID_METALFIX_VERSION_SHIFT 0
#define MCDE_PID_METALFIX_VERSION_MASK 0x000000FF
@ -293,7 +256,6 @@ static int mcde_probe(struct platform_device *pdev)
struct component_match *match = NULL;
struct resource *res;
u32 pid;
u32 val;
int irq;
int ret;
int i;
@ -305,9 +267,6 @@ static int mcde_probe(struct platform_device *pdev)
mcde->dev = dev;
platform_set_drvdata(pdev, drm);
/* Enable continuous updates: this is what Linux' framebuffer expects */
mcde->oneshot_mode = false;
/* First obtain and turn on the main power */
mcde->epod = devm_regulator_get(dev, "epod");
if (IS_ERR(mcde->epod)) {
@ -405,27 +364,7 @@ static int mcde_probe(struct platform_device *pdev)
goto clk_disable;
}
/* Set up the main control, watermark level at 7 */
val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT;
/* 24 bits DPI: connect LSB Ch B to D[0:7] */
val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT;
/* TV out: connect LSB Ch B to D[8:15] */
val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT;
/* Don't care about this muxing */
val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT;
/* 24 bits DPI: connect MID Ch B to D[24:31] */
val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT;
/* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */
val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT;
/* Syncmux bits zero: DPI channel A and B on output pins A and B resp */
writel(val, mcde->regs + MCDE_CONF0);
/* Enable automatic clock gating */
val = readl(mcde->regs + MCDE_CR);
val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN;
writel(val, mcde->regs + MCDE_CR);
/* Clear any pending interrupts */
/* Disable and clear any pending interrupts */
mcde_display_disable_irqs(mcde);
writel(0, mcde->regs + MCDE_IMSCERR);
writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR);
@ -455,12 +394,28 @@ static int mcde_probe(struct platform_device *pdev)
ret = PTR_ERR(match);
goto clk_disable;
}
/*
* Perform an invasive reset of the MCDE and all blocks by
* cutting the power to the subsystem, then bring it back up
* later when we enable the display as a result of
* component_master_add_with_match().
*/
ret = regulator_disable(mcde->epod);
if (ret) {
dev_err(dev, "can't disable EPOD regulator\n");
return ret;
}
/* Wait 50 ms so we are sure we cut the power */
usleep_range(50000, 70000);
ret = component_master_add_with_match(&pdev->dev, &mcde_drm_comp_ops,
match);
if (ret) {
dev_err(dev, "failed to add component master\n");
goto clk_disable;
}
return 0;
clk_disable:

View file

@ -43,6 +43,7 @@ struct mcde_dsi {
struct drm_bridge *bridge_out;
struct mipi_dsi_host dsi_host;
struct mipi_dsi_device *mdsi;
const struct drm_display_mode *mode;
struct clk *hs_clk;
struct clk *lp_clk;
unsigned long hs_freq;
@ -148,9 +149,22 @@ static void mcde_dsi_attach_to_mcde(struct mcde_dsi *d)
{
d->mcde->mdsi = d->mdsi;
d->mcde->video_mode = !!(d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO);
/* Enable use of the TE signal for all command mode panels */
d->mcde->te_sync = !d->mcde->video_mode;
/*
* Select the way the DSI data flow is pushing to the display:
* currently we just support video or command mode depending
* on the type of display. Video mode defaults to using the
* formatter itself for synchronization (stateless video panel).
*
* FIXME: add flags to struct mipi_dsi_device .flags to indicate
* displays that require BTA (bus turn around) so we can handle
* such displays as well. Figure out how to properly handle
* single frame on-demand updates with DRM for command mode
* displays (MCDE_COMMAND_ONESHOT_FLOW).
*/
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO)
d->mcde->flow_mode = MCDE_VIDEO_FORMATTER_FLOW;
else
d->mcde->flow_mode = MCDE_COMMAND_TE_FLOW;
}
static int mcde_dsi_host_attach(struct mipi_dsi_host *host,
@ -194,15 +208,97 @@ static int mcde_dsi_host_detach(struct mipi_dsi_host *host,
(type == MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM) || \
(type == MIPI_DSI_DCS_READ))
static int mcde_dsi_execute_transfer(struct mcde_dsi *d,
const struct mipi_dsi_msg *msg)
{
const u32 loop_delay_us = 10; /* us */
u32 loop_counter;
size_t txlen = msg->tx_len;
size_t rxlen = msg->rx_len;
int i;
u32 val;
int ret;
writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR);
writel(~0, d->regs + DSI_CMD_MODE_STS_CLR);
/* Send command */
writel(1, d->regs + DSI_DIRECT_CMD_SEND);
loop_counter = 1000 * 1000 / loop_delay_us;
if (MCDE_DSI_HOST_IS_READ(msg->type)) {
/* Read command */
while (!(readl(d->regs + DSI_DIRECT_CMD_STS) &
(DSI_DIRECT_CMD_STS_READ_COMPLETED |
DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR))
&& --loop_counter)
usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
if (!loop_counter) {
dev_err(d->dev, "DSI read timeout!\n");
/* Set exit code and retry */
return -ETIME;
}
} else {
/* Writing only */
while (!(readl(d->regs + DSI_DIRECT_CMD_STS) &
DSI_DIRECT_CMD_STS_WRITE_COMPLETED)
&& --loop_counter)
usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
if (!loop_counter) {
/* Set exit code and retry */
dev_err(d->dev, "DSI write timeout!\n");
return -ETIME;
}
}
val = readl(d->regs + DSI_DIRECT_CMD_STS);
if (val & DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR) {
dev_err(d->dev, "read completed with error\n");
writel(1, d->regs + DSI_DIRECT_CMD_RD_INIT);
return -EIO;
}
if (val & DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED) {
val >>= DSI_DIRECT_CMD_STS_ACK_VAL_SHIFT;
dev_err(d->dev, "error during transmission: %04x\n",
val);
return -EIO;
}
if (!MCDE_DSI_HOST_IS_READ(msg->type)) {
/* Return number of bytes written */
ret = txlen;
} else {
/* OK this is a read command, get the response */
u32 rdsz;
u32 rddat;
u8 *rx = msg->rx_buf;
rdsz = readl(d->regs + DSI_DIRECT_CMD_RD_PROPERTY);
rdsz &= DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_MASK;
rddat = readl(d->regs + DSI_DIRECT_CMD_RDDAT);
if (rdsz < rxlen) {
dev_err(d->dev, "read error, requested %zd got %d\n",
rxlen, rdsz);
return -EIO;
}
/* FIXME: read more than 4 bytes */
for (i = 0; i < 4 && i < rxlen; i++)
rx[i] = (rddat >> (i * 8)) & 0xff;
ret = rdsz;
}
/* Successful transmission */
return ret;
}
static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
{
struct mcde_dsi *d = host_to_mcde_dsi(host);
const u32 loop_delay_us = 10; /* us */
const u8 *tx = msg->tx_buf;
u32 loop_counter;
size_t txlen = msg->tx_len;
size_t rxlen = msg->rx_len;
unsigned int retries = 0;
u32 val;
int ret;
int i;
@ -268,72 +364,16 @@ static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
writel(val, d->regs + DSI_DIRECT_CMD_WRDAT3);
}
writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR);
writel(~0, d->regs + DSI_CMD_MODE_STS_CLR);
/* Send command */
writel(1, d->regs + DSI_DIRECT_CMD_SEND);
loop_counter = 1000 * 1000 / loop_delay_us;
if (MCDE_DSI_HOST_IS_READ(msg->type)) {
/* Read command */
while (!(readl(d->regs + DSI_DIRECT_CMD_STS) &
(DSI_DIRECT_CMD_STS_READ_COMPLETED |
DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR))
&& --loop_counter)
usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
if (!loop_counter) {
dev_err(d->dev, "DSI read timeout!\n");
return -ETIME;
}
} else {
/* Writing only */
while (!(readl(d->regs + DSI_DIRECT_CMD_STS) &
DSI_DIRECT_CMD_STS_WRITE_COMPLETED)
&& --loop_counter)
usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
if (!loop_counter) {
dev_err(d->dev, "DSI write timeout!\n");
return -ETIME;
}
}
val = readl(d->regs + DSI_DIRECT_CMD_STS);
if (val & DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR) {
dev_err(d->dev, "read completed with error\n");
writel(1, d->regs + DSI_DIRECT_CMD_RD_INIT);
return -EIO;
}
if (val & DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED) {
val >>= DSI_DIRECT_CMD_STS_ACK_VAL_SHIFT;
dev_err(d->dev, "error during transmission: %04x\n",
val);
return -EIO;
}
if (!MCDE_DSI_HOST_IS_READ(msg->type)) {
/* Return number of bytes written */
ret = txlen;
} else {
/* OK this is a read command, get the response */
u32 rdsz;
u32 rddat;
u8 *rx = msg->rx_buf;
rdsz = readl(d->regs + DSI_DIRECT_CMD_RD_PROPERTY);
rdsz &= DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_MASK;
rddat = readl(d->regs + DSI_DIRECT_CMD_RDDAT);
if (rdsz < rxlen) {
dev_err(d->dev, "read error, requested %zd got %d\n",
rxlen, rdsz);
return -EIO;
}
/* FIXME: read more than 4 bytes */
for (i = 0; i < 4 && i < rxlen; i++)
rx[i] = (rddat >> (i * 8)) & 0xff;
ret = rdsz;
while (retries < 3) {
ret = mcde_dsi_execute_transfer(d, msg);
if (ret >= 0)
break;
retries++;
}
if (ret < 0 && retries)
dev_err(d->dev, "gave up after %d retries\n", retries);
/* Clear any errors */
writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR);
writel(~0, d->regs + DSI_CMD_MODE_STS_CLR);
@ -799,10 +839,11 @@ static void mcde_dsi_start(struct mcde_dsi *d)
/* Command mode, clear IF1 ID */
val = readl(d->regs + DSI_CMD_MODE_CTL);
/*
* If we enable low-power mode here, with
* val |= DSI_CMD_MODE_CTL_IF1_LP_EN
* If we enable low-power mode here,
* then display updates become really slow.
*/
if (d->mdsi->mode_flags & MIPI_DSI_MODE_LPM)
val |= DSI_CMD_MODE_CTL_IF1_LP_EN;
val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
writel(val, d->regs + DSI_CMD_MODE_CTL);
@ -811,23 +852,11 @@ static void mcde_dsi_start(struct mcde_dsi *d)
dev_info(d->dev, "DSI link enabled\n");
}
static void mcde_dsi_bridge_enable(struct drm_bridge *bridge)
{
struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
u32 val;
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
/* Enable video mode */
val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
}
dev_info(d->dev, "enable DSI master\n");
};
static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
/*
* Notice that this is called from inside the display controller
* and not from the bridge callbacks.
*/
void mcde_dsi_enable(struct drm_bridge *bridge)
{
struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
unsigned long hs_freq, lp_freq;
@ -871,7 +900,25 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
dev_info(d->dev, "DSI HS clock rate %lu Hz\n",
d->hs_freq);
/* Assert RESET through the PRCMU, active low */
/* FIXME: which DSI block? */
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
usleep_range(100, 200);
/* De-assert RESET again */
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
PRCM_DSI_SW_RESET_DSI0_SW_RESETN,
PRCM_DSI_SW_RESET_DSI0_SW_RESETN);
/* Start up the hardware */
mcde_dsi_start(d);
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
/* Set up the video mode from the DRM mode */
mcde_dsi_setup_video_mode(d, d->mode);
/* Put IF1 into video mode */
val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE;
@ -887,17 +934,25 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC;
val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA;
writel(val, d->regs + DSI_VID_MODE_STS_CTL);
/* Enable video mode */
val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
} else {
/* Command mode, clear IF1 ID */
val = readl(d->regs + DSI_CMD_MODE_CTL);
/*
* If we enable low-power mode here with
* val |= DSI_CMD_MODE_CTL_IF1_LP_EN
* If we enable low-power mode here
* the display updates become really slow.
*/
if (d->mdsi->mode_flags & MIPI_DSI_MODE_LPM)
val |= DSI_CMD_MODE_CTL_IF1_LP_EN;
val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
writel(val, d->regs + DSI_CMD_MODE_CTL);
}
dev_info(d->dev, "enabled MCDE DSI master\n");
}
static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
@ -911,13 +966,12 @@ static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
return;
}
d->mode = mode;
dev_info(d->dev, "set DSI master to %dx%d %u Hz %s mode\n",
mode->hdisplay, mode->vdisplay, mode->clock * 1000,
(d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD"
);
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO)
mcde_dsi_setup_video_mode(d, mode);
}
static void mcde_dsi_wait_for_command_mode_stop(struct mcde_dsi *d)
@ -961,14 +1015,15 @@ static void mcde_dsi_wait_for_video_mode_stop(struct mcde_dsi *d)
}
}
static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
/*
* Notice that this is called from inside the display controller
* and not from the bridge callbacks.
*/
void mcde_dsi_disable(struct drm_bridge *bridge)
{
struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
u32 val;
/* Disable all error interrupts */
writel(0, d->regs + DSI_VID_MODE_STS_CTL);
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
/* Stop video mode */
val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
@ -980,7 +1035,14 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
mcde_dsi_wait_for_command_mode_stop(d);
}
/* Stop clocks */
/*
* Stop clocks and terminate any DSI traffic here so the panel can
* send commands to shut down the display using DSI direct write until
* this point.
*/
/* Disable all error interrupts */
writel(0, d->regs + DSI_VID_MODE_STS_CTL);
clk_disable_unprepare(d->hs_clk);
clk_disable_unprepare(d->lp_clk);
}
@ -1010,9 +1072,6 @@ static int mcde_dsi_bridge_attach(struct drm_bridge *bridge,
static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = {
.attach = mcde_dsi_bridge_attach,
.mode_set = mcde_dsi_bridge_mode_set,
.disable = mcde_dsi_bridge_disable,
.enable = mcde_dsi_bridge_enable,
.pre_enable = mcde_dsi_bridge_pre_enable,
};
static int mcde_dsi_bind(struct device *dev, struct device *master,
@ -1048,21 +1107,6 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
return PTR_ERR(d->lp_clk);
}
/* Assert RESET through the PRCMU, active low */
/* FIXME: which DSI block? */
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
usleep_range(100, 200);
/* De-assert RESET again */
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
PRCM_DSI_SW_RESET_DSI0_SW_RESETN,
PRCM_DSI_SW_RESET_DSI0_SW_RESETN);
/* Start up the hardware */
mcde_dsi_start(d);
/* Look for a panel as a child to this node */
for_each_available_child_of_node(dev->of_node, child) {
panel = of_drm_find_panel(child);

View file

@ -1,13 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_MGAG200
tristate "Kernel modesetting driver for MGA G200 server engines"
tristate "Matrox G200"
depends on DRM && PCI && MMU
select DRM_GEM_SHMEM_HELPER
select DRM_KMS_HELPER
help
This is a KMS driver for the MGA G200 server chips, it
does not support the original MGA G200 or any of the desktop
chips. It requires 0.3.0 of the modesetting userspace driver,
and a version of mga driver that will fail on KMS enabled
devices.
This is a KMS driver for Matrox G200 chips. It supports the original
MGA G200 desktop chips and the server variants. It requires 0.3.0
of the modesetting userspace driver, and a version of mga driver
that will fail on KMS enabled devices.

View file

@ -9,6 +9,7 @@
#include <linux/console.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
@ -36,6 +37,7 @@ static struct drm_driver mgag200_driver = {
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
.gem_create_object = drm_gem_shmem_create_object_cached,
DRM_GEM_SHMEM_DRIVER_OPS,
};
@ -43,18 +45,66 @@ static struct drm_driver mgag200_driver = {
* DRM device
*/
static int mgag200_device_init(struct mga_device *mdev, unsigned long flags)
static bool mgag200_has_sgram(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
int ret, option;
u32 option;
int ret;
mdev->flags = mgag200_flags_from_driver_data(flags);
mdev->type = mgag200_type_from_driver_data(flags);
ret = pci_read_config_dword(dev->pdev, PCI_MGA_OPTION, &option);
if (drm_WARN(dev, ret, "failed to read PCI config dword: %d\n", ret))
return false;
pci_read_config_dword(dev->pdev, PCI_MGA_OPTION, &option);
mdev->has_sdram = !(option & (1 << 14));
return !!(option & PCI_MGA_OPTION_HARDPWMSK);
}
/* BAR 0 is the framebuffer, BAR 1 contains registers */
static int mgag200_regs_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
u32 option, option2;
u8 crtcext3;
switch (mdev->type) {
case G200_PCI:
case G200_AGP:
if (mgag200_has_sgram(mdev))
option = 0x4049cd21;
else
option = 0x40499121;
option2 = 0x00008000;
break;
case G200_SE_A:
case G200_SE_B:
option = 0x40049120;
if (mgag200_has_sgram(mdev))
option |= PCI_MGA_OPTION_HARDPWMSK;
option2 = 0x00008000;
break;
case G200_WB:
case G200_EW3:
option = 0x41049120;
option2 = 0x0000b000;
break;
case G200_EV:
option = 0x00000120;
option2 = 0x0000b000;
break;
case G200_EH:
case G200_EH3:
option = 0x00000120;
option2 = 0x0000b000;
break;
default:
option = 0;
option2 = 0;
}
if (option)
pci_write_config_dword(dev->pdev, PCI_MGA_OPTION, option);
if (option2)
pci_write_config_dword(dev->pdev, PCI_MGA_OPTION2, option2);
/* BAR 1 contains registers */
mdev->rmmio_base = pci_resource_start(dev->pdev, 1);
mdev->rmmio_size = pci_resource_len(dev->pdev, 1);
@ -68,13 +118,164 @@ static int mgag200_device_init(struct mga_device *mdev, unsigned long flags)
if (mdev->rmmio == NULL)
return -ENOMEM;
/* stash G200 SE model number for later use */
if (IS_G200_SE(mdev)) {
mdev->unique_rev_id = RREG32(0x1e24);
drm_dbg(dev, "G200 SE unique revision id is 0x%x\n",
mdev->unique_rev_id);
RREG_ECRT(0x03, crtcext3);
crtcext3 |= MGAREG_CRTCEXT3_MGAMODE;
WREG_ECRT(0x03, crtcext3);
return 0;
}
static void mgag200_g200_interpret_bios(struct mga_device *mdev,
const unsigned char *bios,
size_t size)
{
static const char matrox[] = {'M', 'A', 'T', 'R', 'O', 'X'};
static const unsigned int expected_length[6] = {
0, 64, 64, 64, 128, 128
};
struct drm_device *dev = &mdev->base;
const unsigned char *pins;
unsigned int pins_len, version;
int offset;
int tmp;
/* Test for MATROX string. */
if (size < 45 + sizeof(matrox))
return;
if (memcmp(&bios[45], matrox, sizeof(matrox)) != 0)
return;
/* Get the PInS offset. */
if (size < MGA_BIOS_OFFSET + 2)
return;
offset = (bios[MGA_BIOS_OFFSET + 1] << 8) | bios[MGA_BIOS_OFFSET];
/* Get PInS data structure. */
if (size < offset + 6)
return;
pins = bios + offset;
if (pins[0] == 0x2e && pins[1] == 0x41) {
version = pins[5];
pins_len = pins[2];
} else {
version = 1;
pins_len = pins[0] + (pins[1] << 8);
}
if (version < 1 || version > 5) {
drm_warn(dev, "Unknown BIOS PInS version: %d\n", version);
return;
}
if (pins_len != expected_length[version]) {
drm_warn(dev, "Unexpected BIOS PInS size: %d expected: %d\n",
pins_len, expected_length[version]);
return;
}
if (size < offset + pins_len)
return;
drm_dbg_kms(dev, "MATROX BIOS PInS version %d size: %d found\n",
version, pins_len);
/* Extract the clock values */
switch (version) {
case 1:
tmp = pins[24] + (pins[25] << 8);
if (tmp)
mdev->model.g200.pclk_max = tmp * 10;
break;
case 2:
if (pins[41] != 0xff)
mdev->model.g200.pclk_max = (pins[41] + 100) * 1000;
break;
case 3:
if (pins[36] != 0xff)
mdev->model.g200.pclk_max = (pins[36] + 100) * 1000;
if (pins[52] & 0x20)
mdev->model.g200.ref_clk = 14318;
break;
case 4:
if (pins[39] != 0xff)
mdev->model.g200.pclk_max = pins[39] * 4 * 1000;
if (pins[92] & 0x01)
mdev->model.g200.ref_clk = 14318;
break;
case 5:
tmp = pins[4] ? 8000 : 6000;
if (pins[123] != 0xff)
mdev->model.g200.pclk_min = pins[123] * tmp;
if (pins[38] != 0xff)
mdev->model.g200.pclk_max = pins[38] * tmp;
if (pins[110] & 0x01)
mdev->model.g200.ref_clk = 14318;
break;
default:
break;
}
}
static void mgag200_g200_init_refclk(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
unsigned char __iomem *rom;
unsigned char *bios;
size_t size;
mdev->model.g200.pclk_min = 50000;
mdev->model.g200.pclk_max = 230000;
mdev->model.g200.ref_clk = 27050;
rom = pci_map_rom(dev->pdev, &size);
if (!rom)
return;
bios = vmalloc(size);
if (!bios)
goto out;
memcpy_fromio(bios, rom, size);
if (size != 0 && bios[0] == 0x55 && bios[1] == 0xaa)
mgag200_g200_interpret_bios(mdev, bios, size);
drm_dbg_kms(dev, "pclk_min: %ld pclk_max: %ld ref_clk: %ld\n",
mdev->model.g200.pclk_min, mdev->model.g200.pclk_max,
mdev->model.g200.ref_clk);
vfree(bios);
out:
pci_unmap_rom(dev->pdev, rom);
}
static void mgag200_g200se_init_unique_id(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
/* stash G200 SE model number for later use */
mdev->model.g200se.unique_rev_id = RREG32(0x1e24);
drm_dbg(dev, "G200 SE unique revision id is 0x%x\n",
mdev->model.g200se.unique_rev_id);
}
static int mgag200_device_init(struct mga_device *mdev, unsigned long flags)
{
struct drm_device *dev = &mdev->base;
int ret;
mdev->flags = mgag200_flags_from_driver_data(flags);
mdev->type = mgag200_type_from_driver_data(flags);
ret = mgag200_regs_init(mdev);
if (ret)
return ret;
if (mdev->type == G200_PCI || mdev->type == G200_AGP)
mgag200_g200_init_refclk(mdev);
else if (IS_G200_SE(mdev))
mgag200_g200se_init_unique_id(mdev);
ret = mgag200_mm_init(mdev);
if (ret)
return ret;
@ -116,6 +317,8 @@ mgag200_device_create(struct pci_dev *pdev, unsigned long flags)
*/
static const struct pci_device_id mgag200_pciidlist[] = {
{ PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI },
{ PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_AGP },
{ PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
G200_SE_A | MGAG200_FLAG_HW_BUG_NO_STARTADD},
{ PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },

View file

@ -38,6 +38,8 @@
#define RREG32(reg) ioread32(((void __iomem *)mdev->rmmio) + (reg))
#define WREG32(reg, v) iowrite32(v, ((void __iomem *)mdev->rmmio) + (reg))
#define MGA_BIOS_OFFSET 0x7ffc
#define ATTR_INDEX 0x1fc0
#define ATTR_DATA 0x1fc1
@ -129,6 +131,8 @@ struct mga_mc {
};
enum mga_type {
G200_PCI,
G200_AGP,
G200_SE_A,
G200_SE_B,
G200_WB,
@ -161,14 +165,23 @@ struct mga_device {
size_t vram_fb_available;
enum mga_type type;
int has_sdram;
int bpp_shifts[4];
int fb_mtrr;
/* SE model number stored in reg 0x1e24 */
u32 unique_rev_id;
union {
struct {
long ref_clk;
long pclk_min;
long pclk_max;
} g200;
struct {
/* SE model number stored in reg 0x1e24 */
u32 unique_rev_id;
} g200se;
} model;
struct mga_connector connector;
struct drm_simple_display_pipe display_pipe;

View file

@ -90,9 +90,17 @@ static void mgag200_mm_release(struct drm_device *dev, void *ptr)
int mgag200_mm_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
u8 misc;
resource_size_t start, len;
int ret;
WREG_ECRT(0x04, 0x00);
misc = RREG8(MGA_MISC_IN);
misc |= MGAREG_MISC_RAMMAPEN |
MGAREG_MISC_HIGH_PG_SEL;
WREG8(MGA_MISC_OUT, misc);
/* BAR 0 is VRAM */
start = pci_resource_start(dev->pdev, 0);
len = pci_resource_len(dev->pdev, 0);

View file

@ -9,7 +9,6 @@
*/
#include <linux/delay.h>
#include <linux/pci.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_state_helper.h>
@ -109,10 +108,82 @@ static inline void mga_wait_busy(struct mga_device *mdev)
} while ((status & 0x01) && time_before(jiffies, timeout));
}
/*
* PLL setup
*/
static int mgag200_g200_set_plls(struct mga_device *mdev, long clock)
{
struct drm_device *dev = &mdev->base;
const int post_div_max = 7;
const int in_div_min = 1;
const int in_div_max = 6;
const int feed_div_min = 7;
const int feed_div_max = 127;
u8 testm, testn;
u8 n = 0, m = 0, p, s;
long f_vco;
long computed;
long delta, tmp_delta;
long ref_clk = mdev->model.g200.ref_clk;
long p_clk_min = mdev->model.g200.pclk_min;
long p_clk_max = mdev->model.g200.pclk_max;
if (clock > p_clk_max) {
drm_err(dev, "Pixel Clock %ld too high\n", clock);
return 1;
}
if (clock < p_clk_min >> 3)
clock = p_clk_min >> 3;
f_vco = clock;
for (p = 0;
p <= post_div_max && f_vco < p_clk_min;
p = (p << 1) + 1, f_vco <<= 1)
;
delta = clock;
for (testm = in_div_min; testm <= in_div_max; testm++) {
for (testn = feed_div_min; testn <= feed_div_max; testn++) {
computed = ref_clk * (testn + 1) / (testm + 1);
if (computed < f_vco)
tmp_delta = f_vco - computed;
else
tmp_delta = computed - f_vco;
if (tmp_delta < delta) {
delta = tmp_delta;
m = testm;
n = testn;
}
}
}
f_vco = ref_clk * (n + 1) / (m + 1);
if (f_vco < 100000)
s = 0;
else if (f_vco < 140000)
s = 1;
else if (f_vco < 180000)
s = 2;
else
s = 3;
drm_dbg_kms(dev, "clock: %ld vco: %ld m: %d n: %d p: %d s: %d\n",
clock, f_vco, m, n, p, s);
WREG_DAC(MGA1064_PIX_PLLC_M, m);
WREG_DAC(MGA1064_PIX_PLLC_N, n);
WREG_DAC(MGA1064_PIX_PLLC_P, (p | (s << 3)));
return 0;
}
#define P_ARRAY_SIZE 9
static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
{
u32 unique_rev_id = mdev->model.g200se.unique_rev_id;
unsigned int vcomax, vcomin, pllreffreq;
unsigned int delta, tmpdelta, permitteddelta;
unsigned int testp, testm, testn;
@ -122,7 +193,7 @@ static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
unsigned int fvv;
unsigned int i;
if (mdev->unique_rev_id <= 0x03) {
if (unique_rev_id <= 0x03) {
m = n = p = 0;
vcomax = 320000;
@ -220,7 +291,7 @@ static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
WREG_DAC(MGA1064_PIX_PLLC_N, n);
WREG_DAC(MGA1064_PIX_PLLC_P, p);
if (mdev->unique_rev_id >= 0x04) {
if (unique_rev_id >= 0x04) {
WREG_DAC(0x1a, 0x09);
msleep(20);
WREG_DAC(0x1a, 0x01);
@ -717,6 +788,9 @@ static int mgag200_crtc_set_plls(struct mga_device *mdev, long clock)
u8 misc;
switch(mdev->type) {
case G200_PCI:
case G200_AGP:
return mgag200_g200_set_plls(mdev, clock);
case G200_SE_A:
case G200_SE_B:
return mga_g200se_set_plls(mdev, clock);
@ -877,45 +951,6 @@ static void mgag200_set_startadd(struct mga_device *mdev,
WREG_ECRT(0x00, crtcext0);
}
static void mgag200_set_pci_regs(struct mga_device *mdev)
{
uint32_t option = 0, option2 = 0;
struct drm_device *dev = &mdev->base;
switch (mdev->type) {
case G200_SE_A:
case G200_SE_B:
if (mdev->has_sdram)
option = 0x40049120;
else
option = 0x4004d120;
option2 = 0x00008000;
break;
case G200_WB:
case G200_EW3:
option = 0x41049120;
option2 = 0x0000b000;
break;
case G200_EV:
option = 0x00000120;
option2 = 0x0000b000;
break;
case G200_EH:
case G200_EH3:
option = 0x00000120;
option2 = 0x0000b000;
break;
case G200_ER:
break;
}
if (option)
pci_write_config_dword(dev->pdev, PCI_MGA_OPTION, option);
if (option2)
pci_write_config_dword(dev->pdev, PCI_MGA_OPTION2, option2);
}
static void mgag200_set_dac_regs(struct mga_device *mdev)
{
size_t i;
@ -933,6 +968,12 @@ static void mgag200_set_dac_regs(struct mga_device *mdev)
};
switch (mdev->type) {
case G200_PCI:
case G200_AGP:
dacvalue[MGA1064_SYS_PLL_M] = 0x04;
dacvalue[MGA1064_SYS_PLL_N] = 0x2D;
dacvalue[MGA1064_SYS_PLL_P] = 0x19;
break;
case G200_SE_A:
case G200_SE_B:
dacvalue[MGA1064_VREF_CTL] = 0x03;
@ -986,9 +1027,8 @@ static void mgag200_set_dac_regs(struct mga_device *mdev)
static void mgag200_init_regs(struct mga_device *mdev)
{
u8 crtc11, crtcext3, crtcext4, misc;
u8 crtc11, misc;
mgag200_set_pci_regs(mdev);
mgag200_set_dac_regs(mdev);
WREG_SEQ(2, 0x0f);
@ -1002,14 +1042,6 @@ static void mgag200_init_regs(struct mga_device *mdev)
WREG_CRT(14, 0);
WREG_CRT(15, 0);
RREG_ECRT(0x03, crtcext3);
crtcext3 |= BIT(7); /* enable MGA mode */
crtcext4 = 0x00;
WREG_ECRT(0x03, crtcext3);
WREG_ECRT(0x04, crtcext4);
RREG_CRT(0x11, crtc11);
crtc11 &= ~(MGAREG_CRTC11_CRTCPROTECT |
MGAREG_CRTC11_VINTEN |
@ -1023,9 +1055,7 @@ static void mgag200_init_regs(struct mga_device *mdev)
WREG_ECRT(0x34, 0x5);
misc = RREG8(MGA_MISC_IN);
misc |= MGAREG_MISC_IOADSEL |
MGAREG_MISC_RAMMAPEN |
MGAREG_MISC_HIGH_PG_SEL;
misc |= MGAREG_MISC_IOADSEL;
WREG8(MGA_MISC_OUT, misc);
}
@ -1234,12 +1264,13 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev,
const struct drm_display_mode *mode,
const struct drm_framebuffer *fb)
{
u32 unique_rev_id = mdev->model.g200se.unique_rev_id;
unsigned int hiprilvl;
u8 crtcext6;
if (mdev->unique_rev_id >= 0x04) {
if (unique_rev_id >= 0x04) {
hiprilvl = 0;
} else if (mdev->unique_rev_id >= 0x02) {
} else if (unique_rev_id >= 0x02) {
unsigned int bpp;
unsigned long mb;
@ -1264,7 +1295,7 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev,
else
hiprilvl = 5;
} else if (mdev->unique_rev_id >= 0x01) {
} else if (unique_rev_id >= 0x01) {
hiprilvl = 3;
} else {
hiprilvl = 4;
@ -1388,7 +1419,9 @@ static enum drm_mode_status mga_vga_mode_valid(struct drm_connector *connector,
int bpp = 32;
if (IS_G200_SE(mdev)) {
if (mdev->unique_rev_id == 0x01) {
u32 unique_rev_id = mdev->model.g200se.unique_rev_id;
if (unique_rev_id == 0x01) {
if (mode->hdisplay > 1600)
return MODE_VIRTUAL_X;
if (mode->vdisplay > 1200)
@ -1396,7 +1429,7 @@ static enum drm_mode_status mga_vga_mode_valid(struct drm_connector *connector,
if (mga_vga_calculate_mode_bandwidth(mode, bpp)
> (24400 * 1024))
return MODE_BANDWIDTH;
} else if (mdev->unique_rev_id == 0x02) {
} else if (unique_rev_id == 0x02) {
if (mode->hdisplay > 1920)
return MODE_VIRTUAL_X;
if (mode->vdisplay > 1200)

View file

@ -256,6 +256,8 @@
#define MGAREG_CRTCEXT1_VSYNCOFF BIT(5)
#define MGAREG_CRTCEXT1_HSYNCOFF BIT(4)
#define MGAREG_CRTCEXT3_MGAMODE BIT(7)
/* Cursor X and Y position */
#define MGA_CURPOSXL 0x3c0c
#define MGA_CURPOSXH 0x3c0d
@ -282,6 +284,8 @@
#define PCI_MGA_OPTION2 0x50
#define PCI_MGA_OPTION3 0x54
#define PCI_MGA_OPTION_HARDPWMSK BIT(14)
#define RAMDAC_OFFSET 0x3c00
/* TVP3026 direct registers */

View file

@ -50,14 +50,9 @@ static int mdp4_lvds_connector_get_modes(struct drm_connector *connector)
struct drm_panel *panel = mdp4_lvds_connector->panel;
int ret = 0;
if (panel) {
drm_panel_attach(panel, connector);
if (panel)
ret = drm_panel_get_modes(panel, connector);
drm_panel_detach(panel);
}
return ret;
}

View file

@ -328,7 +328,6 @@ static int dsi_mgr_connector_get_modes(struct drm_connector *connector)
* In dual DSI mode, we have one connector that can be
* attached to the drm_panel.
*/
drm_panel_attach(panel, connector);
num = drm_panel_get_modes(panel, connector);
if (!num)
return 0;

View file

@ -5,7 +5,7 @@ config DRM_MXS
Choose this option to select drivers for MXS FB devices
config DRM_MXSFB
tristate "i.MX23/i.MX28/i.MX6SX MXSFB LCD controller"
tristate "i.MX (e)LCDIF LCD controller"
depends on DRM && OF
depends on COMMON_CLK
select DRM_MXS
@ -13,8 +13,10 @@ config DRM_MXSFB
select DRM_KMS_FB_HELPER
select DRM_KMS_CMA_HELPER
select DRM_PANEL
select DRM_PANEL_BRIDGE
help
Choose this option if you have an i.MX23/i.MX28/i.MX6SX MXSFB
LCD controller.
Choose this option if you have an LCDIF or eLCDIF LCD controller.
Those devices are found in various i.MX SoC (including i.MX23,
i.MX28, i.MX6SX, i.MX7 and i.MX8M).
If M is selected the module will be called mxsfb.

View file

@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o
mxsfb-y := mxsfb_drv.o mxsfb_kms.o
obj-$(CONFIG_DRM_MXSFB) += mxsfb.o

View file

@ -1,343 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
*
* This code is based on drivers/video/fbdev/mxsfb.c :
* Copyright (C) 2010 Juergen Beisert, Pengutronix
* Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
*/
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/of_graph.h>
#include <linux/platform_data/simplefb.h>
#include <video/videomode.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include "mxsfb_drv.h"
#include "mxsfb_regs.h"
#define MXS_SET_ADDR 0x4
#define MXS_CLR_ADDR 0x8
#define MODULE_CLKGATE BIT(30)
#define MODULE_SFTRST BIT(31)
/* 1 second delay should be plenty of time for block reset */
#define RESET_TIMEOUT 1000000
static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
{
return (val & mxsfb->devdata->hs_wdth_mask) <<
mxsfb->devdata->hs_wdth_shift;
}
/* Setup the MXSFB registers for decoding the pixels out of the framebuffer */
static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb)
{
struct drm_crtc *crtc = &mxsfb->pipe.crtc;
struct drm_device *drm = crtc->dev;
const u32 format = crtc->primary->state->fb->format->format;
u32 ctrl, ctrl1;
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
/*
* WARNING: The bus width, CTRL_SET_BUS_WIDTH(), is configured to
* match the selected mode here. This differs from the original
* MXSFB driver, which had the option to configure the bus width
* to arbitrary value. This limitation should not pose an issue.
*/
/* CTRL1 contains IRQ config and status bits, preserve those. */
ctrl1 = readl(mxsfb->base + LCDC_CTRL1);
ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;
switch (format) {
case DRM_FORMAT_RGB565:
dev_dbg(drm->dev, "Setting up RGB565 mode\n");
ctrl |= CTRL_SET_WORD_LENGTH(0);
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);
break;
case DRM_FORMAT_XRGB8888:
dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
ctrl |= CTRL_SET_WORD_LENGTH(3);
/* Do not use packed pixels = one pixel per word instead. */
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7);
break;
default:
dev_err(drm->dev, "Unhandled pixel format %08x\n", format);
return -EINVAL;
}
writel(ctrl1, mxsfb->base + LCDC_CTRL1);
writel(ctrl, mxsfb->base + LCDC_CTRL);
return 0;
}
static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb)
{
struct drm_crtc *crtc = &mxsfb->pipe.crtc;
struct drm_device *drm = crtc->dev;
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
u32 reg;
reg = readl(mxsfb->base + LCDC_CTRL);
if (mxsfb->connector->display_info.num_bus_formats)
bus_format = mxsfb->connector->display_info.bus_formats[0];
DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",
bus_format);
reg &= ~CTRL_BUS_WIDTH_MASK;
switch (bus_format) {
case MEDIA_BUS_FMT_RGB565_1X16:
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_16BIT);
break;
case MEDIA_BUS_FMT_RGB666_1X18:
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_18BIT);
break;
case MEDIA_BUS_FMT_RGB888_1X24:
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_24BIT);
break;
default:
dev_err(drm->dev, "Unknown media bus format %d\n", bus_format);
break;
}
writel(reg, mxsfb->base + LCDC_CTRL);
}
static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)
{
u32 reg;
if (mxsfb->clk_disp_axi)
clk_prepare_enable(mxsfb->clk_disp_axi);
clk_prepare_enable(mxsfb->clk);
/* If it was disabled, re-enable the mode again */
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET);
/* Enable the SYNC signals first, then the DMA engine */
reg = readl(mxsfb->base + LCDC_VDCTRL4);
reg |= VDCTRL4_SYNC_SIGNALS_ON;
writel(reg, mxsfb->base + LCDC_VDCTRL4);
writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET);
}
static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
{
u32 reg;
/*
* Even if we disable the controller here, it will still continue
* until its FIFOs are running out of data
*/
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR);
readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN),
0, 1000);
reg = readl(mxsfb->base + LCDC_VDCTRL4);
reg &= ~VDCTRL4_SYNC_SIGNALS_ON;
writel(reg, mxsfb->base + LCDC_VDCTRL4);
clk_disable_unprepare(mxsfb->clk);
if (mxsfb->clk_disp_axi)
clk_disable_unprepare(mxsfb->clk_disp_axi);
}
/*
* Clear the bit and poll it cleared. This is usually called with
* a reset address and mask being either SFTRST(bit 31) or CLKGATE
* (bit 30).
*/
static int clear_poll_bit(void __iomem *addr, u32 mask)
{
u32 reg;
writel(mask, addr + MXS_CLR_ADDR);
return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
}
static int mxsfb_reset_block(void __iomem *reset_addr)
{
int ret;
ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
if (ret)
return ret;
writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
if (ret)
return ret;
return clear_poll_bit(reset_addr, MODULE_CLKGATE);
}
static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb)
{
struct drm_framebuffer *fb = mxsfb->pipe.plane.state->fb;
struct drm_gem_cma_object *gem;
if (!fb)
return 0;
gem = drm_fb_cma_get_gem_obj(fb, 0);
if (!gem)
return 0;
return gem->paddr;
}
static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
{
struct drm_device *drm = mxsfb->pipe.crtc.dev;
struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode;
u32 bus_flags = mxsfb->connector->display_info.bus_flags;
u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;
int err;
/*
* It seems, you can't re-program the controller if it is still
* running. This may lead to shifted pictures (FIFO issue?), so
* first stop the controller and drain its FIFOs.
*/
/* Mandatory eLCDIF reset as per the Reference Manual */
err = mxsfb_reset_block(mxsfb->base);
if (err)
return;
/* Clear the FIFOs */
writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
err = mxsfb_set_pixel_fmt(mxsfb);
if (err)
return;
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
if (mxsfb->bridge && mxsfb->bridge->timings)
bus_flags = mxsfb->bridge->timings->input_bus_flags;
DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
m->crtc_clock,
(int)(clk_get_rate(mxsfb->clk) / 1000));
DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",
bus_flags);
DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) |
TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay),
mxsfb->base + mxsfb->devdata->transfer_count);
vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start;
vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */
VDCTRL0_VSYNC_PERIOD_UNIT |
VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len);
if (m->flags & DRM_MODE_FLAG_PHSYNC)
vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;
if (m->flags & DRM_MODE_FLAG_PVSYNC)
vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
/* Make sure Data Enable is high active by default */
if (!(bus_flags & DRM_BUS_FLAG_DE_LOW))
vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
/*
* DRM_BUS_FLAG_PIXDATA_DRIVE_ defines are controller centric,
* controllers VDCTRL0_DOTCLK is display centric.
* Drive on positive edge -> display samples on falling edge
* DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING
*/
if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);
mxsfb_set_bus_fmt(mxsfb);
/* Frame length in lines. */
writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);
/* Line length in units of clocks or pixels. */
hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start;
writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) |
VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal),
mxsfb->base + LCDC_VDCTRL2);
writel(SET_HOR_WAIT_CNT(m->crtc_htotal - m->crtc_hsync_start) |
SET_VERT_WAIT_CNT(m->crtc_vtotal - m->crtc_vsync_start),
mxsfb->base + LCDC_VDCTRL3);
writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay),
mxsfb->base + LCDC_VDCTRL4);
}
void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb)
{
dma_addr_t paddr;
mxsfb_enable_axi_clk(mxsfb);
mxsfb_crtc_mode_set_nofb(mxsfb);
/* Write cur_buf as well to avoid an initial corrupt frame */
paddr = mxsfb_get_fb_paddr(mxsfb);
if (paddr) {
writel(paddr, mxsfb->base + mxsfb->devdata->cur_buf);
writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
}
mxsfb_enable_controller(mxsfb);
}
void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb)
{
mxsfb_disable_controller(mxsfb);
mxsfb_disable_axi_clk(mxsfb);
}
void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb,
struct drm_plane_state *state)
{
struct drm_simple_display_pipe *pipe = &mxsfb->pipe;
struct drm_crtc *crtc = &pipe->crtc;
struct drm_pending_vblank_event *event;
dma_addr_t paddr;
spin_lock_irq(&crtc->dev->event_lock);
event = crtc->state->event;
if (event) {
crtc->state->event = NULL;
if (drm_crtc_vblank_get(crtc) == 0) {
drm_crtc_arm_vblank_event(crtc, event);
} else {
drm_crtc_send_vblank_event(crtc, event);
}
}
spin_unlock_irq(&crtc->dev->event_lock);
paddr = mxsfb_get_fb_paddr(mxsfb);
if (paddr) {
mxsfb_enable_axi_clk(mxsfb);
writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
mxsfb_disable_axi_clk(mxsfb);
}
}

View file

@ -9,30 +9,24 @@
*/
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/dma-mapping.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/dma-resv.h>
#include <linux/spinlock.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_bridge.h>
#include <drm/drm_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_irq.h>
#include <drm/drm_mode_config.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include "mxsfb_drv.h"
@ -41,6 +35,11 @@
enum mxsfb_devtype {
MXSFB_V3,
MXSFB_V4,
/*
* Starting at i.MX6 the hardware version register is gone, use the
* i.MX family number as the version.
*/
MXSFB_V6,
};
static const struct mxsfb_devdata mxsfb_devdata[] = {
@ -48,38 +47,28 @@ static const struct mxsfb_devdata mxsfb_devdata[] = {
.transfer_count = LCDC_V3_TRANSFER_COUNT,
.cur_buf = LCDC_V3_CUR_BUF,
.next_buf = LCDC_V3_NEXT_BUF,
.debug0 = LCDC_V3_DEBUG0,
.hs_wdth_mask = 0xff,
.hs_wdth_shift = 24,
.ipversion = 3,
.has_overlay = false,
},
[MXSFB_V4] = {
.transfer_count = LCDC_V4_TRANSFER_COUNT,
.cur_buf = LCDC_V4_CUR_BUF,
.next_buf = LCDC_V4_NEXT_BUF,
.debug0 = LCDC_V4_DEBUG0,
.hs_wdth_mask = 0x3fff,
.hs_wdth_shift = 18,
.ipversion = 4,
.has_overlay = false,
},
[MXSFB_V6] = {
.transfer_count = LCDC_V4_TRANSFER_COUNT,
.cur_buf = LCDC_V4_CUR_BUF,
.next_buf = LCDC_V4_NEXT_BUF,
.hs_wdth_mask = 0x3fff,
.hs_wdth_shift = 18,
.has_overlay = true,
},
};
static const uint32_t mxsfb_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB565
};
static const uint64_t mxsfb_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
};
static struct mxsfb_drm_private *
drm_pipe_to_mxsfb_drm_private(struct drm_simple_display_pipe *pipe)
{
return container_of(pipe, struct mxsfb_drm_private, pipe);
}
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb)
{
if (mxsfb->clk_axi)
@ -102,101 +91,51 @@ static const struct drm_mode_config_helper_funcs mxsfb_mode_config_helpers = {
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
};
static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb)
{
struct drm_connector *connector;
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
struct drm_device *drm = pipe->plane.dev;
struct drm_device *drm = mxsfb->drm;
struct drm_connector_list_iter iter;
struct drm_panel *panel;
struct drm_bridge *bridge;
int ret;
if (!mxsfb->connector) {
list_for_each_entry(connector,
&drm->mode_config.connector_list,
head)
if (connector->encoder == &mxsfb->pipe.encoder) {
mxsfb->connector = connector;
break;
}
ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
&bridge);
if (ret)
return ret;
if (panel) {
bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel,
DRM_MODE_CONNECTOR_DPI);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
}
if (!mxsfb->connector) {
dev_warn(drm->dev, "No connector attached, using default\n");
mxsfb->connector = &mxsfb->panel_connector;
if (!bridge)
return -ENODEV;
ret = drm_bridge_attach(&mxsfb->encoder, bridge, NULL, 0);
if (ret) {
DRM_DEV_ERROR(drm->dev,
"failed to attach bridge: %d\n", ret);
return ret;
}
pm_runtime_get_sync(drm->dev);
drm_panel_prepare(mxsfb->panel);
mxsfb_crtc_enable(mxsfb);
drm_panel_enable(mxsfb->panel);
}
mxsfb->bridge = bridge;
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe)
{
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
struct drm_device *drm = pipe->plane.dev;
struct drm_crtc *crtc = &pipe->crtc;
struct drm_pending_vblank_event *event;
drm_panel_disable(mxsfb->panel);
mxsfb_crtc_disable(mxsfb);
drm_panel_unprepare(mxsfb->panel);
pm_runtime_put_sync(drm->dev);
spin_lock_irq(&drm->event_lock);
event = crtc->state->event;
if (event) {
crtc->state->event = NULL;
drm_crtc_send_vblank_event(crtc, event);
}
spin_unlock_irq(&drm->event_lock);
if (mxsfb->connector != &mxsfb->panel_connector)
mxsfb->connector = NULL;
}
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state)
{
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
mxsfb_plane_atomic_update(mxsfb, plane_state);
}
static int mxsfb_pipe_enable_vblank(struct drm_simple_display_pipe *pipe)
{
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
/* Clear and enable VBLANK IRQ */
mxsfb_enable_axi_clk(mxsfb);
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
mxsfb_disable_axi_clk(mxsfb);
/*
* Get hold of the connector. This is a bit of a hack, until the bridge
* API gives us bus flags and formats.
*/
drm_connector_list_iter_begin(drm, &iter);
mxsfb->connector = drm_connector_list_iter_next(&iter);
drm_connector_list_iter_end(&iter);
return 0;
}
static void mxsfb_pipe_disable_vblank(struct drm_simple_display_pipe *pipe)
{
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
/* Disable and clear VBLANK IRQ */
mxsfb_enable_axi_clk(mxsfb);
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
mxsfb_disable_axi_clk(mxsfb);
}
static struct drm_simple_display_pipe_funcs mxsfb_funcs = {
.enable = mxsfb_pipe_enable,
.disable = mxsfb_pipe_disable,
.update = mxsfb_pipe_update,
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
.enable_vblank = mxsfb_pipe_enable_vblank,
.disable_vblank = mxsfb_pipe_disable_vblank,
};
static int mxsfb_load(struct drm_device *drm)
static int mxsfb_load(struct drm_device *drm,
const struct mxsfb_devdata *devdata)
{
struct platform_device *pdev = to_platform_device(drm->dev);
struct mxsfb_drm_private *mxsfb;
@ -207,8 +146,9 @@ static int mxsfb_load(struct drm_device *drm)
if (!mxsfb)
return -ENOMEM;
mxsfb->drm = drm;
drm->dev_private = mxsfb;
mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
mxsfb->devdata = devdata;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mxsfb->base = devm_ioremap_resource(drm->dev, res);
@ -233,52 +173,30 @@ static int mxsfb_load(struct drm_device *drm)
pm_runtime_enable(drm->dev);
/* Modeset init */
drm_mode_config_init(drm);
ret = mxsfb_kms_init(mxsfb);
if (ret < 0) {
dev_err(drm->dev, "Failed to initialize KMS pipeline\n");
goto err_vblank;
}
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (ret < 0) {
dev_err(drm->dev, "Failed to initialise vblank\n");
goto err_vblank;
}
/* Modeset init */
drm_mode_config_init(drm);
/* Start with vertical blanking interrupt reporting disabled. */
drm_crtc_vblank_off(&mxsfb->crtc);
ret = mxsfb_create_output(drm);
if (ret < 0) {
dev_err(drm->dev, "Failed to create outputs\n");
ret = mxsfb_attach_bridge(mxsfb);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
mxsfb_formats, ARRAY_SIZE(mxsfb_formats),
mxsfb_modifiers, mxsfb->connector);
if (ret < 0) {
dev_err(drm->dev, "Cannot setup simple display pipe\n");
goto err_vblank;
}
/*
* Attach panel only if there is one.
* If there is no panel attach, it must be a bridge. In this case, we
* need a reference to its connector for a proper initialization.
* We will do this check in pipe->enable(), since the connector won't
* be attached to an encoder until then.
*/
if (mxsfb->panel) {
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
if (ret) {
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
goto err_vblank;
}
} else if (mxsfb->bridge) {
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
mxsfb->bridge);
if (ret) {
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
goto err_vblank;
}
}
drm->mode_config.min_width = MXSFB_MIN_XRES;
drm->mode_config.min_height = MXSFB_MIN_YRES;
drm->mode_config.max_width = MXSFB_MAX_XRES;
@ -294,7 +212,7 @@ static int mxsfb_load(struct drm_device *drm)
if (ret < 0) {
dev_err(drm->dev, "Failed to install IRQ handler\n");
goto err_irq;
goto err_vblank;
}
drm_kms_helper_poll_init(drm);
@ -305,8 +223,6 @@ static int mxsfb_load(struct drm_device *drm)
return 0;
err_irq:
drm_panel_detach(mxsfb->panel);
err_vblank:
pm_runtime_disable(drm->dev);
@ -327,11 +243,13 @@ static void mxsfb_unload(struct drm_device *drm)
pm_runtime_disable(drm->dev);
}
static void mxsfb_irq_preinstall(struct drm_device *drm)
static void mxsfb_irq_disable(struct drm_device *drm)
{
struct mxsfb_drm_private *mxsfb = drm->dev_private;
mxsfb_pipe_disable_vblank(&mxsfb->pipe);
mxsfb_enable_axi_clk(mxsfb);
mxsfb->crtc.funcs->disable_vblank(&mxsfb->crtc);
mxsfb_disable_axi_clk(mxsfb);
}
static irqreturn_t mxsfb_irq_handler(int irq, void *data)
@ -340,17 +258,13 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data)
struct mxsfb_drm_private *mxsfb = drm->dev_private;
u32 reg;
mxsfb_enable_axi_clk(mxsfb);
reg = readl(mxsfb->base + LCDC_CTRL1);
if (reg & CTRL1_CUR_FRAME_DONE_IRQ)
drm_crtc_handle_vblank(&mxsfb->pipe.crtc);
drm_crtc_handle_vblank(&mxsfb->crtc);
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
mxsfb_disable_axi_clk(mxsfb);
return IRQ_HANDLED;
}
@ -359,8 +273,8 @@ DEFINE_DRM_GEM_CMA_FOPS(fops);
static struct drm_driver mxsfb_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.irq_handler = mxsfb_irq_handler,
.irq_preinstall = mxsfb_irq_preinstall,
.irq_uninstall = mxsfb_irq_preinstall,
.irq_preinstall = mxsfb_irq_disable,
.irq_uninstall = mxsfb_irq_disable,
DRM_GEM_CMA_DRIVER_OPS,
.fops = &fops,
.name = "mxsfb-drm",
@ -370,18 +284,10 @@ static struct drm_driver mxsfb_driver = {
.minor = 0,
};
static const struct platform_device_id mxsfb_devtype[] = {
{ .name = "imx23-fb", .driver_data = MXSFB_V3, },
{ .name = "imx28-fb", .driver_data = MXSFB_V4, },
{ .name = "imx6sx-fb", .driver_data = MXSFB_V4, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, mxsfb_devtype);
static const struct of_device_id mxsfb_dt_ids[] = {
{ .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },
{ .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },
{ .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devtype[2], },
{ .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devdata[MXSFB_V3], },
{ .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devdata[MXSFB_V4], },
{ .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devdata[MXSFB_V6], },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxsfb_dt_ids);
@ -396,14 +302,11 @@ static int mxsfb_probe(struct platform_device *pdev)
if (!pdev->dev.of_node)
return -ENODEV;
if (of_id)
pdev->id_entry = of_id->data;
drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
ret = mxsfb_load(drm);
ret = mxsfb_load(drm, of_id->data);
if (ret)
goto err_free;
@ -457,7 +360,6 @@ static const struct dev_pm_ops mxsfb_pm_ops = {
static struct platform_driver mxsfb_platform_driver = {
.probe = mxsfb_probe,
.remove = mxsfb_remove,
.id_table = mxsfb_devtype,
.driver = {
.name = "mxsfb",
.of_match_table = mxsfb_dt_ids,

View file

@ -8,14 +8,20 @@
#ifndef __MXSFB_DRV_H__
#define __MXSFB_DRV_H__
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_encoder.h>
#include <drm/drm_plane.h>
struct clk;
struct mxsfb_devdata {
unsigned int transfer_count;
unsigned int cur_buf;
unsigned int next_buf;
unsigned int debug0;
unsigned int hs_wdth_mask;
unsigned int hs_wdth_shift;
unsigned int ipversion;
unsigned int transfer_count;
unsigned int cur_buf;
unsigned int next_buf;
unsigned int hs_wdth_mask;
unsigned int hs_wdth_shift;
bool has_overlay;
};
struct mxsfb_drm_private {
@ -26,22 +32,26 @@ struct mxsfb_drm_private {
struct clk *clk_axi;
struct clk *clk_disp_axi;
struct drm_simple_display_pipe pipe;
struct drm_connector panel_connector;
struct drm_device *drm;
struct {
struct drm_plane primary;
struct drm_plane overlay;
} planes;
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector *connector;
struct drm_panel *panel;
struct drm_bridge *bridge;
};
int mxsfb_setup_crtc(struct drm_device *dev);
int mxsfb_create_output(struct drm_device *dev);
static inline struct mxsfb_drm_private *
to_mxsfb_drm_private(struct drm_device *drm)
{
return drm->dev_private;
}
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb);
void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb);
void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb);
void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb);
void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb,
struct drm_plane_state *state);
int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb);
#endif /* __MXSFB_DRV_H__ */

View file

@ -0,0 +1,571 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
*
* This code is based on drivers/video/fbdev/mxsfb.c :
* Copyright (C) 2010 Juergen Beisert, Pengutronix
* Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_encoder.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_plane.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_vblank.h>
#include "mxsfb_drv.h"
#include "mxsfb_regs.h"
/* 1 second delay should be plenty of time for block reset */
#define RESET_TIMEOUT 1000000
/* -----------------------------------------------------------------------------
* CRTC
*/
static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
{
return (val & mxsfb->devdata->hs_wdth_mask) <<
mxsfb->devdata->hs_wdth_shift;
}
/*
* Setup the MXSFB registers for decoding the pixels out of the framebuffer and
* outputting them on the bus.
*/
static void mxsfb_set_formats(struct mxsfb_drm_private *mxsfb)
{
struct drm_device *drm = mxsfb->drm;
const u32 format = mxsfb->crtc.primary->state->fb->format->format;
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
u32 ctrl, ctrl1;
if (mxsfb->connector->display_info.num_bus_formats)
bus_format = mxsfb->connector->display_info.bus_formats[0];
DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",
bus_format);
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
/* CTRL1 contains IRQ config and status bits, preserve those. */
ctrl1 = readl(mxsfb->base + LCDC_CTRL1);
ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;
switch (format) {
case DRM_FORMAT_RGB565:
dev_dbg(drm->dev, "Setting up RGB565 mode\n");
ctrl |= CTRL_WORD_LENGTH_16;
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);
break;
case DRM_FORMAT_XRGB8888:
dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
ctrl |= CTRL_WORD_LENGTH_24;
/* Do not use packed pixels = one pixel per word instead. */
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7);
break;
}
switch (bus_format) {
case MEDIA_BUS_FMT_RGB565_1X16:
ctrl |= CTRL_BUS_WIDTH_16;
break;
case MEDIA_BUS_FMT_RGB666_1X18:
ctrl |= CTRL_BUS_WIDTH_18;
break;
case MEDIA_BUS_FMT_RGB888_1X24:
ctrl |= CTRL_BUS_WIDTH_24;
break;
default:
dev_err(drm->dev, "Unknown media bus format %d\n", bus_format);
break;
}
writel(ctrl1, mxsfb->base + LCDC_CTRL1);
writel(ctrl, mxsfb->base + LCDC_CTRL);
}
static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)
{
u32 reg;
if (mxsfb->clk_disp_axi)
clk_prepare_enable(mxsfb->clk_disp_axi);
clk_prepare_enable(mxsfb->clk);
/* If it was disabled, re-enable the mode again */
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET);
/* Enable the SYNC signals first, then the DMA engine */
reg = readl(mxsfb->base + LCDC_VDCTRL4);
reg |= VDCTRL4_SYNC_SIGNALS_ON;
writel(reg, mxsfb->base + LCDC_VDCTRL4);
writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET);
}
static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
{
u32 reg;
/*
* Even if we disable the controller here, it will still continue
* until its FIFOs are running out of data
*/
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR);
readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN),
0, 1000);
reg = readl(mxsfb->base + LCDC_VDCTRL4);
reg &= ~VDCTRL4_SYNC_SIGNALS_ON;
writel(reg, mxsfb->base + LCDC_VDCTRL4);
clk_disable_unprepare(mxsfb->clk);
if (mxsfb->clk_disp_axi)
clk_disable_unprepare(mxsfb->clk_disp_axi);
}
/*
* Clear the bit and poll it cleared. This is usually called with
* a reset address and mask being either SFTRST(bit 31) or CLKGATE
* (bit 30).
*/
static int clear_poll_bit(void __iomem *addr, u32 mask)
{
u32 reg;
writel(mask, addr + REG_CLR);
return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
}
static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb)
{
int ret;
ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);
if (ret)
return ret;
writel(CTRL_CLKGATE, mxsfb->base + LCDC_CTRL + REG_CLR);
ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);
if (ret)
return ret;
return clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE);
}
static dma_addr_t mxsfb_get_fb_paddr(struct drm_plane *plane)
{
struct drm_framebuffer *fb = plane->state->fb;
struct drm_gem_cma_object *gem;
if (!fb)
return 0;
gem = drm_fb_cma_get_gem_obj(fb, 0);
if (!gem)
return 0;
return gem->paddr;
}
static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
{
struct drm_device *drm = mxsfb->crtc.dev;
struct drm_display_mode *m = &mxsfb->crtc.state->adjusted_mode;
u32 bus_flags = mxsfb->connector->display_info.bus_flags;
u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;
int err;
/*
* It seems, you can't re-program the controller if it is still
* running. This may lead to shifted pictures (FIFO issue?), so
* first stop the controller and drain its FIFOs.
*/
/* Mandatory eLCDIF reset as per the Reference Manual */
err = mxsfb_reset_block(mxsfb);
if (err)
return;
/* Clear the FIFOs */
writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
if (mxsfb->devdata->has_overlay)
writel(0, mxsfb->base + LCDC_AS_CTRL);
mxsfb_set_formats(mxsfb);
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
if (mxsfb->bridge && mxsfb->bridge->timings)
bus_flags = mxsfb->bridge->timings->input_bus_flags;
DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
m->crtc_clock,
(int)(clk_get_rate(mxsfb->clk) / 1000));
DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",
bus_flags);
DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) |
TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay),
mxsfb->base + mxsfb->devdata->transfer_count);
vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start;
vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */
VDCTRL0_VSYNC_PERIOD_UNIT |
VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len);
if (m->flags & DRM_MODE_FLAG_PHSYNC)
vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;
if (m->flags & DRM_MODE_FLAG_PVSYNC)
vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
/* Make sure Data Enable is high active by default */
if (!(bus_flags & DRM_BUS_FLAG_DE_LOW))
vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
/*
* DRM_BUS_FLAG_PIXDATA_DRIVE_ defines are controller centric,
* controllers VDCTRL0_DOTCLK is display centric.
* Drive on positive edge -> display samples on falling edge
* DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING
*/
if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);
/* Frame length in lines. */
writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);
/* Line length in units of clocks or pixels. */
hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start;
writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) |
VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal),
mxsfb->base + LCDC_VDCTRL2);
writel(SET_HOR_WAIT_CNT(m->crtc_htotal - m->crtc_hsync_start) |
SET_VERT_WAIT_CNT(m->crtc_vtotal - m->crtc_vsync_start),
mxsfb->base + LCDC_VDCTRL3);
writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay),
mxsfb->base + LCDC_VDCTRL4);
}
static int mxsfb_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
bool has_primary = state->plane_mask &
drm_plane_mask(crtc->primary);
/* The primary plane has to be enabled when the CRTC is active. */
if (state->active && !has_primary)
return -EINVAL;
/* TODO: Is this needed ? */
return drm_atomic_add_affected_planes(state->state, crtc);
}
static void mxsfb_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct drm_pending_vblank_event *event;
event = crtc->state->event;
crtc->state->event = NULL;
if (!event)
return;
spin_lock_irq(&crtc->dev->event_lock);
if (drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irq(&crtc->dev->event_lock);
}
static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
struct drm_device *drm = mxsfb->drm;
dma_addr_t paddr;
pm_runtime_get_sync(drm->dev);
mxsfb_enable_axi_clk(mxsfb);
drm_crtc_vblank_on(crtc);
mxsfb_crtc_mode_set_nofb(mxsfb);
/* Write cur_buf as well to avoid an initial corrupt frame */
paddr = mxsfb_get_fb_paddr(crtc->primary);
if (paddr) {
writel(paddr, mxsfb->base + mxsfb->devdata->cur_buf);
writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
}
mxsfb_enable_controller(mxsfb);
}
static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
struct drm_device *drm = mxsfb->drm;
struct drm_pending_vblank_event *event;
mxsfb_disable_controller(mxsfb);
spin_lock_irq(&drm->event_lock);
event = crtc->state->event;
if (event) {
crtc->state->event = NULL;
drm_crtc_send_vblank_event(crtc, event);
}
spin_unlock_irq(&drm->event_lock);
drm_crtc_vblank_off(crtc);
mxsfb_disable_axi_clk(mxsfb);
pm_runtime_put_sync(drm->dev);
}
static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc)
{
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
/* Clear and enable VBLANK IRQ */
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
return 0;
}
static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc)
{
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
/* Disable and clear VBLANK IRQ */
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
}
static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = {
.atomic_check = mxsfb_crtc_atomic_check,
.atomic_flush = mxsfb_crtc_atomic_flush,
.atomic_enable = mxsfb_crtc_atomic_enable,
.atomic_disable = mxsfb_crtc_atomic_disable,
};
static const struct drm_crtc_funcs mxsfb_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = mxsfb_crtc_enable_vblank,
.disable_vblank = mxsfb_crtc_disable_vblank,
};
/* -----------------------------------------------------------------------------
* Encoder
*/
static const struct drm_encoder_funcs mxsfb_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
/* -----------------------------------------------------------------------------
* Planes
*/
static int mxsfb_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *plane_state)
{
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
struct drm_crtc_state *crtc_state;
crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
&mxsfb->crtc);
return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING,
false, true);
}
static void mxsfb_plane_primary_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_pstate)
{
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
dma_addr_t paddr;
paddr = mxsfb_get_fb_paddr(plane);
if (paddr)
writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
}
static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_pstate)
{
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
struct drm_plane_state *state = plane->state;
dma_addr_t paddr;
u32 ctrl;
paddr = mxsfb_get_fb_paddr(plane);
if (!paddr) {
writel(0, mxsfb->base + LCDC_AS_CTRL);
return;
}
/*
* HACK: The hardware seems to output 64 bytes of data of unknown
* origin, and then to proceed with the framebuffer. Until the reason
* is understood, live with the 16 initial invalid pixels on the first
* line and start 64 bytes within the framebuffer.
*/
paddr += 64;
writel(paddr, mxsfb->base + LCDC_AS_NEXT_BUF);
/*
* If the plane was previously disabled, write LCDC_AS_BUF as well to
* provide the first buffer.
*/
if (!old_pstate->fb)
writel(paddr, mxsfb->base + LCDC_AS_BUF);
ctrl = AS_CTRL_AS_ENABLE | AS_CTRL_ALPHA(255);
switch (state->fb->format->format) {
case DRM_FORMAT_XRGB4444:
ctrl |= AS_CTRL_FORMAT_RGB444 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
case DRM_FORMAT_ARGB4444:
ctrl |= AS_CTRL_FORMAT_ARGB4444 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
break;
case DRM_FORMAT_XRGB1555:
ctrl |= AS_CTRL_FORMAT_RGB555 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
case DRM_FORMAT_ARGB1555:
ctrl |= AS_CTRL_FORMAT_ARGB1555 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
break;
case DRM_FORMAT_RGB565:
ctrl |= AS_CTRL_FORMAT_RGB565 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
case DRM_FORMAT_XRGB8888:
ctrl |= AS_CTRL_FORMAT_RGB888 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
break;
case DRM_FORMAT_ARGB8888:
ctrl |= AS_CTRL_FORMAT_ARGB8888 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
break;
}
writel(ctrl, mxsfb->base + LCDC_AS_CTRL);
}
static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = {
.atomic_check = mxsfb_plane_atomic_check,
.atomic_update = mxsfb_plane_primary_atomic_update,
};
static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = {
.atomic_check = mxsfb_plane_atomic_check,
.atomic_update = mxsfb_plane_overlay_atomic_update,
};
static const struct drm_plane_funcs mxsfb_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
static const uint32_t mxsfb_primary_plane_formats[] = {
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB8888,
};
static const uint32_t mxsfb_overlay_plane_formats[] = {
DRM_FORMAT_XRGB4444,
DRM_FORMAT_ARGB4444,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_ARGB1555,
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
};
static const uint64_t mxsfb_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
};
/* -----------------------------------------------------------------------------
* Initialization
*/
int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb)
{
struct drm_encoder *encoder = &mxsfb->encoder;
struct drm_crtc *crtc = &mxsfb->crtc;
int ret;
drm_plane_helper_add(&mxsfb->planes.primary,
&mxsfb_plane_primary_helper_funcs);
ret = drm_universal_plane_init(mxsfb->drm, &mxsfb->planes.primary, 1,
&mxsfb_plane_funcs,
mxsfb_primary_plane_formats,
ARRAY_SIZE(mxsfb_primary_plane_formats),
mxsfb_modifiers, DRM_PLANE_TYPE_PRIMARY,
NULL);
if (ret)
return ret;
if (mxsfb->devdata->has_overlay) {
drm_plane_helper_add(&mxsfb->planes.overlay,
&mxsfb_plane_overlay_helper_funcs);
ret = drm_universal_plane_init(mxsfb->drm,
&mxsfb->planes.overlay, 1,
&mxsfb_plane_funcs,
mxsfb_overlay_plane_formats,
ARRAY_SIZE(mxsfb_overlay_plane_formats),
mxsfb_modifiers, DRM_PLANE_TYPE_OVERLAY,
NULL);
if (ret)
return ret;
}
drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);
ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
&mxsfb->planes.primary, NULL,
&mxsfb_crtc_funcs, NULL);
if (ret)
return ret;
encoder->possible_crtcs = drm_crtc_mask(crtc);
return drm_encoder_init(mxsfb->drm, encoder, &mxsfb_encoder_funcs,
DRM_MODE_ENCODER_NONE, NULL);
}

Some files were not shown because too many files have changed in this diff Show more