Merge tag 'drm-misc-next-2017-09-20' of git://anongit.freedesktop.org/git/drm-misc into drm-next
UAPI Changes: Cross-subsystem Changes: Core Changes: - DP SDP defines (Ville) - polish for scdc helpers (Thierry Reding) - fix lifetimes for connector/plane state across crtc changes (Maarten Lankhorst). - sparse fixes (Ville+Thierry) - make legacy kms ioctls all interruptible (Maarten) - push edid override into the edid helpers (out of probe helpers) (Jani) - DP ESI defines for link status (DK) Driver Changes: - drm-panel is now in drm-misc! - minor panel-simple cleanups/refactoring by various folks - drm_bridge_add cleanup (Inki Dae) - constify a few i2c_device_id structs (Arvind Yadav) - More patches from Noralf's fb/gem helper cleanup - bridge/synopsis: reset fix (Philippe Cornu) - fix tracepoint include handling in drivers (Thierry) - rockchip: lvds support (Sandy Huang) - move sun4i into drm-misc fold (Maxime Ripard) - sun4i: refactor driver load + support TCON backend/layer muxing (Chen-Yu Tsai) - pl111: support more pl11x variants (Linus Walleij) - bridge/adv7511: robustify probing/edid handling (Lars-Petersen Clausen) New hw support: - S6E63J0X03 panel (Hoegeun Kwon) - OTM8009A panel (Philippe CORNU) - Seiko 43WVF1G panel (Marco Franchi) - tve200 driver (Linus Walleij) Plus assorted of tiny patches all over, including our first outreachy patches from applicants for the winter round! * tag 'drm-misc-next-2017-09-20' of git://anongit.freedesktop.org/git/drm-misc: (101 commits) drm: add backwards compatibility support for drm_kms_helper.edid_firmware drm: handle override and firmware EDID at drm_do_get_edid() level drm/dp: DPCD register defines for link status within ESI field drm/rockchip: Replace dev_* with DRM_DEV_* drm/tinydrm: Drop driver registered message drm/gem-fb-helper: Use debug message on gem lookup failure drm/imx: Use drm_gem_fb_create() and drm_gem_fb_prepare_fb() drm/bridge: adv7511: Constify HDMI CODEC platform data drm/bridge: adv7511: Enable connector polling when no interrupt is specified drm/bridge: adv7511: Remove private copy of the EDID drm/bridge: adv7511: Properly update EDID when no EDID was found drm/crtc: Convert setcrtc ioctl locking to interruptible. drm/atomic: Convert pageflip ioctl locking to interruptible. drm/legacy: Convert setplane ioctl locking to interruptible. drm/legacy: Convert cursor ioctl locking to interruptible. drm/atomic: Convert atomic ioctl locking to interruptible. drm/atomic: Prepare drm_modeset_lock infrastructure for interruptible waiting, v2. drm/tve200: Clean up panel bridging drm/doc: Update todo.rst drm/dp/mst: Sideband message transaction to power up/down nodes ...
This commit is contained in:
commit
29baa82aa5
|
@ -854,7 +854,7 @@
|
|||
The filter can be disabled or changed to another
|
||||
driver later using sysfs.
|
||||
|
||||
drm_kms_helper.edid_firmware=[<connector>:]<file>[,[<connector>:]<file>]
|
||||
drm.edid_firmware=[<connector>:]<file>[,[<connector>:]<file>]
|
||||
Broken monitors, graphic adapters, KVMs and EDIDless
|
||||
panels may send no or incorrect EDID data sets.
|
||||
This parameter allows to specify an EDID data sets
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
* Faraday TV Encoder TVE200
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be one of:
|
||||
"faraday,tve200"
|
||||
"cortina,gemini-tvc", "faraday,tve200"
|
||||
|
||||
- reg: base address and size of the control registers block
|
||||
|
||||
- interrupts: contains an interrupt specifier for the interrupt
|
||||
line from the TVE200
|
||||
|
||||
- clock-names: should contain "PCLK" for the clock line clocking the
|
||||
silicon and "TVE" for the 27MHz clock to the video driver
|
||||
|
||||
- clocks: contains phandle and clock specifier pairs for the entries
|
||||
in the clock-names property. See
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
Optional properties:
|
||||
|
||||
- resets: contains the reset line phandle for the block
|
||||
|
||||
Required sub-nodes:
|
||||
|
||||
- port: describes LCD panel signals, following the common binding
|
||||
for video transmitter interfaces; see
|
||||
Documentation/devicetree/bindings/media/video-interfaces.txt
|
||||
This port should have the properties:
|
||||
reg = <0>;
|
||||
It should have one endpoint connected to a remote endpoint where
|
||||
the display is connected.
|
||||
|
||||
Example:
|
||||
|
||||
display-controller@6a000000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "faraday,tve200";
|
||||
reg = <0x6a000000 0x1000>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_RISING>;
|
||||
resets = <&syscon GEMINI_RESET_TVC>;
|
||||
clocks = <&syscon GEMINI_CLK_GATE_TVC>,
|
||||
<&syscon GEMINI_CLK_TVC>;
|
||||
clock-names = "PCLK", "TVE";
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
display_out: endpoint {
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode)
|
||||
|
||||
The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
|
||||
a MIPI-DSI video interface. Its backlight is managed through the DSI link.
|
||||
|
||||
Required properties:
|
||||
- compatible: "orisetech,otm8009a"
|
||||
- reg: the virtual channel number of a DSI peripheral
|
||||
|
||||
Optional properties:
|
||||
- reset-gpios: a GPIO spec for the reset pin (active low).
|
||||
|
||||
Example:
|
||||
&dsi {
|
||||
...
|
||||
panel@0 {
|
||||
compatible = "orisetech,otm8009a";
|
||||
reg = <0>;
|
||||
reset-gpios = <&gpioh 7 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
Samsung S6E63J0X03 1.63" 320x320 AMOLED panel (interface: MIPI-DSI command mode)
|
||||
|
||||
Required properties:
|
||||
- compatible: "samsung,s6e63j0x03"
|
||||
- reg: the virtual channel number of a DSI peripheral
|
||||
- vdd3-supply: I/O voltage supply
|
||||
- vci-supply: voltage supply for analog circuits
|
||||
- reset-gpios: a GPIO spec for the reset pin (active low)
|
||||
- te-gpios: a GPIO spec for the tearing effect synchronization signal
|
||||
gpio pin (active high)
|
||||
|
||||
Example:
|
||||
&dsi {
|
||||
...
|
||||
|
||||
panel@0 {
|
||||
compatible = "samsung,s6e63j0x03";
|
||||
reg = <0>;
|
||||
vdd3-supply = <&ldo16_reg>;
|
||||
vci-supply = <&ldo20_reg>;
|
||||
reset-gpios = <&gpe0 1 GPIO_ACTIVE_LOW>;
|
||||
te-gpios = <&gpx0 6 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
Seiko Instruments Inc. 4.3" WVGA (800 x RGB x 480) TFT with Touch-Panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "sii,43wvf1g".
|
||||
- "dvdd-supply": 3v3 digital regulator.
|
||||
- "avdd-supply": 5v analog regulator.
|
||||
|
||||
Optional properties:
|
||||
- backlight: phandle for the backlight control.
|
||||
|
||||
Example:
|
||||
|
||||
panel {
|
||||
compatible = "sii,43wvf1g";
|
||||
backlight = <&backlight_display>;
|
||||
dvdd-supply = <®_lcd_3v3>;
|
||||
avdd-supply = <®_lcd_5v>;
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&display_out>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,99 @@
|
|||
Rockchip RK3288 LVDS interface
|
||||
================================
|
||||
|
||||
Required properties:
|
||||
- compatible: matching the soc type, one of
|
||||
- "rockchip,rk3288-lvds";
|
||||
|
||||
- reg: physical base address of the controller and length
|
||||
of memory mapped region.
|
||||
- clocks: must include clock specifiers corresponding to entries in the
|
||||
clock-names property.
|
||||
- clock-names: must contain "pclk_lvds"
|
||||
|
||||
- avdd1v0-supply: regulator phandle for 1.0V analog power
|
||||
- avdd1v8-supply: regulator phandle for 1.8V analog power
|
||||
- avdd3v3-supply: regulator phandle for 3.3V analog power
|
||||
|
||||
- rockchip,grf: phandle to the general register files syscon
|
||||
- rockchip,output: "rgb", "lvds" or "duallvds", This describes the output interface
|
||||
|
||||
Optional properties:
|
||||
- pinctrl-names: must contain a "lcdc" entry.
|
||||
- pinctrl-0: pin control group to be used for this controller.
|
||||
|
||||
Required nodes:
|
||||
|
||||
The lvds has two video ports as described by
|
||||
Documentation/devicetree/bindings/media/video-interfaces.txt
|
||||
Their connections are modeled using the OF graph bindings specified in
|
||||
Documentation/devicetree/bindings/graph.txt.
|
||||
|
||||
- video port 0 for the VOP input, the remote endpoint maybe vopb or vopl
|
||||
- video port 1 for either a panel or subsequent encoder
|
||||
|
||||
the lvds panel described by
|
||||
Documentation/devicetree/bindings/display/panel/simple-panel.txt
|
||||
|
||||
Panel required properties:
|
||||
- ports for remote LVDS output
|
||||
|
||||
Panel optional properties:
|
||||
- data-mapping: should be "vesa-24","jeida-24" or "jeida-18".
|
||||
This describes decribed by:
|
||||
Documentation/devicetree/bindings/display/panel/panel-lvds.txt
|
||||
|
||||
Example:
|
||||
|
||||
lvds_panel: lvds-panel {
|
||||
compatible = "auo,b101ean01";
|
||||
enable-gpios = <&gpio7 21 GPIO_ACTIVE_HIGH>;
|
||||
data-mapping = "jeida-24";
|
||||
|
||||
ports {
|
||||
panel_in_lvds: endpoint {
|
||||
remote-endpoint = <&lvds_out_panel>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
For Rockchip RK3288:
|
||||
|
||||
lvds: lvds@ff96c000 {
|
||||
compatible = "rockchip,rk3288-lvds";
|
||||
rockchip,grf = <&grf>;
|
||||
reg = <0xff96c000 0x4000>;
|
||||
clocks = <&cru PCLK_LVDS_PHY>;
|
||||
clock-names = "pclk_lvds";
|
||||
pinctrl-names = "lcdc";
|
||||
pinctrl-0 = <&lcdc_ctl>;
|
||||
avdd1v0-supply = <&vdd10_lcd>;
|
||||
avdd1v8-supply = <&vcc18_lcd>;
|
||||
avdd3v3-supply = <&vcca_33>;
|
||||
rockchip,output = "rgb";
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
lvds_in: port@0 {
|
||||
reg = <0>;
|
||||
|
||||
lvds_in_vopb: endpoint@0 {
|
||||
reg = <0>;
|
||||
remote-endpoint = <&vopb_out_lvds>;
|
||||
};
|
||||
lvds_in_vopl: endpoint@1 {
|
||||
reg = <1>;
|
||||
remote-endpoint = <&vopl_out_lvds>;
|
||||
};
|
||||
};
|
||||
|
||||
lvds_out: port@1 {
|
||||
reg = <1>;
|
||||
|
||||
lvds_out_panel: endpoint {
|
||||
remote-endpoint = <&panel_in_lvds>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -248,6 +248,7 @@ ontat On Tat Industrial Company
|
|||
opencores OpenCores.org
|
||||
option Option NV
|
||||
ORCL Oracle Corporation
|
||||
orisetech Orise Technology
|
||||
ortustech Ortus Technology Co., Ltd.
|
||||
ovti OmniVision Technologies
|
||||
oxsemi Oxford Semiconductor, Ltd.
|
||||
|
|
|
@ -168,6 +168,61 @@ IOCTL Support on Device Nodes
|
|||
.. kernel-doc:: drivers/gpu/drm/drm_ioctl.c
|
||||
:doc: driver specific ioctls
|
||||
|
||||
Recommended IOCTL Return Values
|
||||
-------------------------------
|
||||
|
||||
In theory a driver's IOCTL callback is only allowed to return very few error
|
||||
codes. In practice it's good to abuse a few more. This section documents common
|
||||
practice within the DRM subsystem:
|
||||
|
||||
ENOENT:
|
||||
Strictly this should only be used when a file doesn't exist e.g. when
|
||||
calling the open() syscall. We reuse that to signal any kind of object
|
||||
lookup failure, e.g. for unknown GEM buffer object handles, unknown KMS
|
||||
object handles and similar cases.
|
||||
|
||||
ENOSPC:
|
||||
Some drivers use this to differentiate "out of kernel memory" from "out
|
||||
of VRAM". Sometimes also applies to other limited gpu resources used for
|
||||
rendering (e.g. when you have a special limited compression buffer).
|
||||
Sometimes resource allocation/reservation issues in command submission
|
||||
IOCTLs are also signalled through EDEADLK.
|
||||
|
||||
Simply running out of kernel/system memory is signalled through ENOMEM.
|
||||
|
||||
EPERM/EACCESS:
|
||||
Returned for an operation that is valid, but needs more privileges.
|
||||
E.g. root-only or much more common, DRM master-only operations return
|
||||
this when when called by unpriviledged clients. There's no clear
|
||||
difference between EACCESS and EPERM.
|
||||
|
||||
ENODEV:
|
||||
Feature (like PRIME, modesetting, GEM) is not supported by the driver.
|
||||
|
||||
ENXIO:
|
||||
Remote failure, either a hardware transaction (like i2c), but also used
|
||||
when the exporting driver of a shared dma-buf or fence doesn't support a
|
||||
feature needed.
|
||||
|
||||
EINTR:
|
||||
DRM drivers assume that userspace restarts all IOCTLs. Any DRM IOCTL can
|
||||
return EINTR and in such a case should be restarted with the IOCTL
|
||||
parameters left unchanged.
|
||||
|
||||
EIO:
|
||||
The GPU died and couldn't be resurrected through a reset. Modesetting
|
||||
hardware failures are signalled through the "link status" connector
|
||||
property.
|
||||
|
||||
EINVAL:
|
||||
Catch-all for anything that is an invalid argument combination which
|
||||
cannot work.
|
||||
|
||||
IOCTL also use other error codes like ETIME, EFAULT, EBUSY, ENOTTY but their
|
||||
usage is in line with the common meanings. The above list tries to just document
|
||||
DRM specific patterns. Note that ENOTTY has the slightly unintuitive meaning of
|
||||
"this IOCTL does not exist", and is used exactly as such in DRM.
|
||||
|
||||
.. kernel-doc:: include/drm/drm_ioctl.h
|
||||
:internal:
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ Linux GPU Driver Developer's Guide
|
|||
pl111
|
||||
tegra
|
||||
tinydrm
|
||||
tve200
|
||||
vc4
|
||||
vga-switcheroo
|
||||
vgaarbiter
|
||||
|
|
|
@ -75,17 +75,6 @@ helpers.
|
|||
|
||||
Contact: Ville Syrjälä, Daniel Vetter, driver maintainers
|
||||
|
||||
Implement deferred fbdev setup in the helper
|
||||
--------------------------------------------
|
||||
|
||||
Many (especially embedded drivers) want to delay fbdev setup until there's a
|
||||
real screen plugged in. This is to avoid the dreaded fallback to the low-res
|
||||
fbdev default. Many drivers have a hacked-up (and often broken) version of this,
|
||||
better to do it once in the shared helpers. Thierry has a patch series, but that
|
||||
one needs to be rebased and final polish applied.
|
||||
|
||||
Contact: Thierry Reding, Daniel Vetter, driver maintainers
|
||||
|
||||
Convert early atomic drivers to async commit helpers
|
||||
----------------------------------------------------
|
||||
|
||||
|
@ -138,6 +127,8 @@ interfaces to fix these issues:
|
|||
the acquire context explicitly on stack and then also pass it down into
|
||||
drivers explicitly so that the legacy-on-atomic functions can use them.
|
||||
|
||||
Except for some driver code this is done.
|
||||
|
||||
* A bunch of the vtable hooks are now in the wrong place: DRM has a split
|
||||
between core vfunc tables (named ``drm_foo_funcs``), which are used to
|
||||
implement the userspace ABI. And then there's the optional hooks for the
|
||||
|
@ -151,6 +142,8 @@ interfaces to fix these issues:
|
|||
connector at runtime. That's almost all of them, and would allow us to get
|
||||
rid of a lot of ``best_encoder`` boilerplate in drivers.
|
||||
|
||||
This was almost done, but new drivers added a few more cases again.
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
Get rid of dev->struct_mutex from GEM drivers
|
||||
|
@ -177,6 +170,17 @@ following drivers still use ``struct_mutex``: ``msm``, ``omapdrm`` and
|
|||
|
||||
Contact: Daniel Vetter, respective driver maintainers
|
||||
|
||||
Convert instances of dev_info/dev_err/dev_warn to their DRM_DEV_* equivalent
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
For drivers which could have multiple instances, it is necessary to
|
||||
differentiate between which is which in the logs. Since DRM_INFO/WARN/ERROR
|
||||
don't do this, drivers used dev_info/warn/err to make this differentiation. We
|
||||
now have DRM_DEV_* variants of the drm print macros, so we can start to convert
|
||||
those drivers back to using drm-formwatted specific log messages.
|
||||
|
||||
Contact: Sean Paul, Maintainer of the driver you plan to convert
|
||||
|
||||
Core refactorings
|
||||
=================
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
==================================
|
||||
drm/tve200 Faraday TV Encoder 200
|
||||
==================================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tve200/tve200_drv.c
|
||||
:doc: Faraday TV Encoder 200
|
10
MAINTAINERS
10
MAINTAINERS
|
@ -4366,6 +4366,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
|
|||
S: Maintained
|
||||
F: drivers/gpu/drm/bochs/
|
||||
|
||||
DRM DRIVER FOR FARADAY TVE200 TV ENCODER
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
S: Maintained
|
||||
F: drivers/gpu/drm/tve200/
|
||||
|
||||
DRM DRIVER FOR INTEL I810 VIDEO CARDS
|
||||
S: Orphan / Obsolete
|
||||
F: drivers/gpu/drm/i810/
|
||||
|
@ -4509,7 +4515,7 @@ L: dri-devel@lists.freedesktop.org
|
|||
S: Supported
|
||||
F: drivers/gpu/drm/sun4i/
|
||||
F: Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux.git
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
|
||||
DRM DRIVERS FOR AMLOGIC SOCS
|
||||
M: Neil Armstrong <narmstrong@baylibre.com>
|
||||
|
@ -4693,7 +4699,7 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
|
|||
DRM PANEL DRIVERS
|
||||
M: Thierry Reding <thierry.reding@gmail.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
T: git git://anongit.freedesktop.org/tegra/linux.git
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
S: Maintained
|
||||
F: drivers/gpu/drm/drm_panel.c
|
||||
F: drivers/gpu/drm/panel/
|
||||
|
|
|
@ -321,8 +321,16 @@ static int sw_sync_debugfs_open(struct inode *inode, struct file *file)
|
|||
static int sw_sync_debugfs_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct sync_timeline *obj = file->private_data;
|
||||
struct sync_pt *pt, *next;
|
||||
|
||||
smp_wmb();
|
||||
spin_lock_irq(&obj->lock);
|
||||
|
||||
list_for_each_entry_safe(pt, next, &obj->pt_list, link) {
|
||||
dma_fence_set_error(&pt->base, -ENOENT);
|
||||
dma_fence_signal_locked(&pt->base);
|
||||
}
|
||||
|
||||
spin_unlock_irq(&obj->lock);
|
||||
|
||||
sync_timeline_put(obj);
|
||||
return 0;
|
||||
|
|
|
@ -110,7 +110,7 @@ config DRM_FBDEV_OVERALLOC
|
|||
|
||||
config DRM_LOAD_EDID_FIRMWARE
|
||||
bool "Allow to specify an EDID data set instead of probing for it"
|
||||
depends on DRM_KMS_HELPER
|
||||
depends on DRM
|
||||
help
|
||||
Say Y here, if you want to use EDID data to be loaded from the
|
||||
/lib/firmware directory or one of the provided built-in
|
||||
|
@ -278,6 +278,8 @@ source "drivers/gpu/drm/tinydrm/Kconfig"
|
|||
|
||||
source "drivers/gpu/drm/pl111/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/tve200/Kconfig"
|
||||
|
||||
# Keep legacy drivers last
|
||||
|
||||
menuconfig DRM_LEGACY
|
||||
|
|
|
@ -28,6 +28,7 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o
|
|||
drm-$(CONFIG_OF) += drm_of.o
|
||||
drm-$(CONFIG_AGP) += drm_agpsupport.o
|
||||
drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
|
||||
drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
|
||||
|
||||
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
|
||||
drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
|
||||
|
@ -36,7 +37,6 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
|
|||
drm_scdc_helper.o drm_gem_framebuffer_helper.o
|
||||
|
||||
drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
|
||||
drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
|
||||
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
|
||||
drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
|
||||
drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
|
||||
|
@ -44,8 +44,6 @@ drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
|
|||
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
|
||||
obj-$(CONFIG_DRM_DEBUG_MM_SELFTEST) += selftests/
|
||||
|
||||
CFLAGS_drm_trace_points.o := -I$(src)
|
||||
|
||||
obj-$(CONFIG_DRM) += drm.o
|
||||
obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
|
||||
obj-$(CONFIG_DRM_ARM) += arm/
|
||||
|
@ -100,3 +98,4 @@ obj-$(CONFIG_DRM_ZTE) += zte/
|
|||
obj-$(CONFIG_DRM_MXSFB) += mxsfb/
|
||||
obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
|
||||
obj-$(CONFIG_DRM_PL111) += pl111/
|
||||
obj-$(CONFIG_DRM_TVE200) += tve200/
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
|
||||
|
@ -32,7 +33,7 @@ static void arcpgu_fb_output_poll_changed(struct drm_device *dev)
|
|||
}
|
||||
|
||||
static const struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.fb_create = drm_gem_fb_create,
|
||||
.output_poll_changed = arcpgu_fb_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
|
||||
#include "hdlcd_drv.h"
|
||||
|
@ -106,7 +107,7 @@ static void hdlcd_fb_output_poll_changed(struct drm_device *drm)
|
|||
}
|
||||
|
||||
static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.fb_create = drm_gem_fb_create,
|
||||
.output_poll_changed = hdlcd_fb_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
|
||||
#include "malidp_drv.h"
|
||||
|
@ -249,7 +250,7 @@ static const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = {
|
|||
};
|
||||
|
||||
static const struct drm_mode_config_funcs malidp_mode_config_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.fb_create = drm_gem_fb_create,
|
||||
.output_poll_changed = malidp_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
|
|
|
@ -4,5 +4,3 @@ armada-y += armada_510.o
|
|||
armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
|
||||
|
||||
obj-$(CONFIG_DRM_ARMADA) := armada.o
|
||||
|
||||
CFLAGS_armada_trace.o := -I$(src)
|
||||
|
|
|
@ -70,8 +70,6 @@ static struct drm_driver armada_drm_driver = {
|
|||
.gem_prime_export = armada_gem_prime_export,
|
||||
.gem_prime_import = armada_gem_prime_import,
|
||||
.dumb_create = armada_gem_dumb_create,
|
||||
.dumb_map_offset = armada_gem_dumb_map_offset,
|
||||
.dumb_destroy = armada_gem_dumb_destroy,
|
||||
.gem_vm_ops = &armada_gem_vm_ops,
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
|
|
|
@ -270,42 +270,6 @@ int armada_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int armada_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
|
||||
uint32_t handle, uint64_t *offset)
|
||||
{
|
||||
struct armada_gem_object *obj;
|
||||
int ret = 0;
|
||||
|
||||
obj = armada_gem_object_lookup(file, handle);
|
||||
if (!obj) {
|
||||
DRM_ERROR("failed to lookup gem object\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Don't allow imported objects to be mapped */
|
||||
if (obj->obj.import_attach) {
|
||||
ret = -EINVAL;
|
||||
goto err_unref;
|
||||
}
|
||||
|
||||
ret = drm_gem_create_mmap_offset(&obj->obj);
|
||||
if (ret == 0) {
|
||||
*offset = drm_vma_node_offset_addr(&obj->obj.vma_node);
|
||||
DRM_DEBUG_DRIVER("handle %#x offset %llx\n", handle, *offset);
|
||||
}
|
||||
|
||||
err_unref:
|
||||
drm_gem_object_unreference_unlocked(&obj->obj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int armada_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
|
||||
uint32_t handle)
|
||||
{
|
||||
return drm_gem_handle_delete(file, handle);
|
||||
}
|
||||
|
||||
/* Private driver gem ioctls */
|
||||
int armada_gem_create_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
|
|
|
@ -35,10 +35,6 @@ struct armada_gem_object *armada_gem_alloc_private_object(struct drm_device *,
|
|||
size_t);
|
||||
int armada_gem_dumb_create(struct drm_file *, struct drm_device *,
|
||||
struct drm_mode_create_dumb *);
|
||||
int armada_gem_dumb_map_offset(struct drm_file *, struct drm_device *,
|
||||
uint32_t, uint64_t *);
|
||||
int armada_gem_dumb_destroy(struct drm_file *, struct drm_device *,
|
||||
uint32_t);
|
||||
struct dma_buf *armada_gem_prime_export(struct drm_device *dev,
|
||||
struct drm_gem_object *obj, int flags);
|
||||
struct drm_gem_object *armada_gem_prime_import(struct drm_device *,
|
||||
|
|
|
@ -62,5 +62,5 @@ TRACE_EVENT(armada_ovl_plane_work,
|
|||
|
||||
/* This part must be outside protection */
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/armada
|
||||
#include <trace/define_trace.h>
|
||||
|
|
|
@ -458,7 +458,7 @@ static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
|
|||
static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev,
|
||||
struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd)
|
||||
{
|
||||
return drm_fb_cma_create(dev, file_priv, mode_cmd);
|
||||
return drm_gem_fb_create(dev, file_priv, mode_cmd);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drmP.h>
|
||||
|
|
|
@ -328,8 +328,6 @@ struct adv7511 {
|
|||
enum adv7511_sync_polarity hsync_polarity;
|
||||
bool rgb;
|
||||
|
||||
struct edid *edid;
|
||||
|
||||
struct gpio_desc *gpio_pd;
|
||||
|
||||
struct regulator_bulk_data *supplies;
|
||||
|
|
|
@ -210,7 +210,7 @@ static const struct hdmi_codec_ops adv7511_codec_ops = {
|
|||
.get_dai_id = adv7511_hdmi_i2s_get_dai_id,
|
||||
};
|
||||
|
||||
static struct hdmi_codec_pdata codec_data = {
|
||||
static const struct hdmi_codec_pdata codec_data = {
|
||||
.ops = &adv7511_codec_ops,
|
||||
.max_i2s_channels = 2,
|
||||
.i2s = 1,
|
||||
|
|
|
@ -199,17 +199,14 @@ static const uint16_t adv7511_csc_ycbcr_to_rgb[] = {
|
|||
|
||||
static void adv7511_set_config_csc(struct adv7511 *adv7511,
|
||||
struct drm_connector *connector,
|
||||
bool rgb)
|
||||
bool rgb, bool hdmi_mode)
|
||||
{
|
||||
struct adv7511_video_config config;
|
||||
bool output_format_422, output_format_ycbcr;
|
||||
unsigned int mode;
|
||||
uint8_t infoframe[17];
|
||||
|
||||
if (adv7511->edid)
|
||||
config.hdmi_mode = drm_detect_hdmi_monitor(adv7511->edid);
|
||||
else
|
||||
config.hdmi_mode = false;
|
||||
config.hdmi_mode = hdmi_mode;
|
||||
|
||||
hdmi_avi_infoframe_init(&config.avi_infoframe);
|
||||
|
||||
|
@ -589,15 +586,14 @@ static int adv7511_get_modes(struct adv7511 *adv7511,
|
|||
if (!adv7511->powered)
|
||||
__adv7511_power_off(adv7511);
|
||||
|
||||
kfree(adv7511->edid);
|
||||
adv7511->edid = edid;
|
||||
if (!edid)
|
||||
return 0;
|
||||
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
count = drm_add_edid_modes(connector, edid);
|
||||
|
||||
adv7511_set_config_csc(adv7511, connector, adv7511->rgb);
|
||||
adv7511_set_config_csc(adv7511, connector, adv7511->rgb,
|
||||
drm_detect_hdmi_monitor(edid));
|
||||
|
||||
kfree(edid);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -833,7 +829,11 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
adv->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
if (adv->i2c_main->irq)
|
||||
adv->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
else
|
||||
adv->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
|
||||
DRM_CONNECTOR_POLL_DISCONNECT;
|
||||
|
||||
ret = drm_connector_init(bridge->dev, &adv->connector,
|
||||
&adv7511_connector_funcs,
|
||||
|
@ -1158,8 +1158,6 @@ static int adv7511_remove(struct i2c_client *i2c)
|
|||
|
||||
i2c_unregister_device(adv7511->i2c_edid);
|
||||
|
||||
kfree(adv7511->edid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,19 +30,20 @@
|
|||
#include <video/mipi_display.h>
|
||||
|
||||
#define DSI_VERSION 0x00
|
||||
|
||||
#define DSI_PWR_UP 0x04
|
||||
#define RESET 0
|
||||
#define POWERUP BIT(0)
|
||||
|
||||
#define DSI_CLKMGR_CFG 0x08
|
||||
#define TO_CLK_DIVIDSION(div) (((div) & 0xff) << 8)
|
||||
#define TX_ESC_CLK_DIVIDSION(div) (((div) & 0xff) << 0)
|
||||
#define TO_CLK_DIVISION(div) (((div) & 0xff) << 8)
|
||||
#define TX_ESC_CLK_DIVISION(div) ((div) & 0xff)
|
||||
|
||||
#define DSI_DPI_VCID 0x0c
|
||||
#define DPI_VID(vid) (((vid) & 0x3) << 0)
|
||||
#define DPI_VCID(vcid) ((vcid) & 0x3)
|
||||
|
||||
#define DSI_DPI_COLOR_CODING 0x10
|
||||
#define EN18_LOOSELY BIT(8)
|
||||
#define LOOSELY18_EN BIT(8)
|
||||
#define DPI_COLOR_CODING_16BIT_1 0x0
|
||||
#define DPI_COLOR_CODING_16BIT_2 0x1
|
||||
#define DPI_COLOR_CODING_16BIT_3 0x2
|
||||
|
@ -61,22 +62,25 @@
|
|||
#define OUTVACT_LPCMD_TIME(p) (((p) & 0xff) << 16)
|
||||
#define INVACT_LPCMD_TIME(p) ((p) & 0xff)
|
||||
|
||||
#define DSI_DBI_VCID 0x1c
|
||||
#define DSI_DBI_CFG 0x20
|
||||
#define DSI_DBI_PARTITIONING_EN 0x24
|
||||
#define DSI_DBI_CMDSIZE 0x28
|
||||
|
||||
#define DSI_PCKHDL_CFG 0x2c
|
||||
#define EN_CRC_RX BIT(4)
|
||||
#define EN_ECC_RX BIT(3)
|
||||
#define EN_BTA BIT(2)
|
||||
#define EN_EOTP_RX BIT(1)
|
||||
#define EN_EOTP_TX BIT(0)
|
||||
#define CRC_RX_EN BIT(4)
|
||||
#define ECC_RX_EN BIT(3)
|
||||
#define BTA_EN BIT(2)
|
||||
#define EOTP_RX_EN BIT(1)
|
||||
#define EOTP_TX_EN BIT(0)
|
||||
|
||||
#define DSI_GEN_VCID 0x30
|
||||
|
||||
#define DSI_MODE_CFG 0x34
|
||||
#define ENABLE_VIDEO_MODE 0
|
||||
#define ENABLE_CMD_MODE BIT(0)
|
||||
|
||||
#define DSI_VID_MODE_CFG 0x38
|
||||
#define FRAME_BTA_ACK BIT(14)
|
||||
#define ENABLE_LOW_POWER (0x3f << 8)
|
||||
#define ENABLE_LOW_POWER_MASK (0x3f << 8)
|
||||
#define VID_MODE_TYPE_NON_BURST_SYNC_PULSES 0x0
|
||||
|
@ -85,8 +89,13 @@
|
|||
#define VID_MODE_TYPE_MASK 0x3
|
||||
|
||||
#define DSI_VID_PKT_SIZE 0x3c
|
||||
#define VID_PKT_SIZE(p) (((p) & 0x3fff) << 0)
|
||||
#define VID_PKT_MAX_SIZE 0x3fff
|
||||
#define VID_PKT_SIZE(p) ((p) & 0x3fff)
|
||||
|
||||
#define DSI_VID_NUM_CHUNKS 0x40
|
||||
#define VID_NUM_CHUNKS(c) ((c) & 0x1fff)
|
||||
|
||||
#define DSI_VID_NULL_SIZE 0x44
|
||||
#define VID_NULL_SIZE(b) ((b) & 0x1fff)
|
||||
|
||||
#define DSI_VID_HSA_TIME 0x48
|
||||
#define DSI_VID_HBP_TIME 0x4c
|
||||
|
@ -95,6 +104,8 @@
|
|||
#define DSI_VID_VBP_LINES 0x58
|
||||
#define DSI_VID_VFP_LINES 0x5c
|
||||
#define DSI_VID_VACTIVE_LINES 0x60
|
||||
#define DSI_EDPI_CMD_SIZE 0x64
|
||||
|
||||
#define DSI_CMD_MODE_CFG 0x68
|
||||
#define MAX_RD_PKT_SIZE_LP BIT(24)
|
||||
#define DCS_LW_TX_LP BIT(19)
|
||||
|
@ -108,8 +119,8 @@
|
|||
#define GEN_SW_2P_TX_LP BIT(10)
|
||||
#define GEN_SW_1P_TX_LP BIT(9)
|
||||
#define GEN_SW_0P_TX_LP BIT(8)
|
||||
#define EN_ACK_RQST BIT(1)
|
||||
#define EN_TEAR_FX BIT(0)
|
||||
#define ACK_RQST_EN BIT(1)
|
||||
#define TEAR_FX_EN BIT(0)
|
||||
|
||||
#define CMD_MODE_ALL_LP (MAX_RD_PKT_SIZE_LP | \
|
||||
DCS_LW_TX_LP | \
|
||||
|
@ -125,27 +136,31 @@
|
|||
GEN_SW_0P_TX_LP)
|
||||
|
||||
#define DSI_GEN_HDR 0x6c
|
||||
/* TODO These 2 defines will be reworked thanks to mipi_dsi_create_packet() */
|
||||
#define GEN_HDATA(data) (((data) & 0xffff) << 8)
|
||||
#define GEN_HDATA_MASK (0xffff << 8)
|
||||
#define GEN_HTYPE(type) (((type) & 0xff) << 0)
|
||||
#define GEN_HTYPE_MASK 0xff
|
||||
|
||||
#define DSI_GEN_PLD_DATA 0x70
|
||||
|
||||
#define DSI_CMD_PKT_STATUS 0x74
|
||||
#define GEN_CMD_EMPTY BIT(0)
|
||||
#define GEN_CMD_FULL BIT(1)
|
||||
#define GEN_PLD_W_EMPTY BIT(2)
|
||||
#define GEN_PLD_W_FULL BIT(3)
|
||||
#define GEN_PLD_R_EMPTY BIT(4)
|
||||
#define GEN_PLD_R_FULL BIT(5)
|
||||
#define GEN_RD_CMD_BUSY BIT(6)
|
||||
#define GEN_PLD_R_FULL BIT(5)
|
||||
#define GEN_PLD_R_EMPTY BIT(4)
|
||||
#define GEN_PLD_W_FULL BIT(3)
|
||||
#define GEN_PLD_W_EMPTY BIT(2)
|
||||
#define GEN_CMD_FULL BIT(1)
|
||||
#define GEN_CMD_EMPTY BIT(0)
|
||||
|
||||
#define DSI_TO_CNT_CFG 0x78
|
||||
#define HSTX_TO_CNT(p) (((p) & 0xffff) << 16)
|
||||
#define LPRX_TO_CNT(p) ((p) & 0xffff)
|
||||
|
||||
#define DSI_HS_RD_TO_CNT 0x7c
|
||||
#define DSI_LP_RD_TO_CNT 0x80
|
||||
#define DSI_HS_WR_TO_CNT 0x84
|
||||
#define DSI_LP_WR_TO_CNT 0x88
|
||||
#define DSI_BTA_TO_CNT 0x8c
|
||||
|
||||
#define DSI_LPCLK_CTRL 0x94
|
||||
#define AUTO_CLKLANE_CTRL BIT(1)
|
||||
#define PHY_TXREQUESTCLKHS BIT(0)
|
||||
|
@ -154,6 +169,7 @@
|
|||
#define PHY_CLKHS2LP_TIME(lbcc) (((lbcc) & 0x3ff) << 16)
|
||||
#define PHY_CLKLP2HS_TIME(lbcc) ((lbcc) & 0x3ff)
|
||||
|
||||
/* TODO Next register is slightly different between 1.30 & 1.31 IP version */
|
||||
#define DSI_PHY_TMR_CFG 0x9c
|
||||
#define PHY_HS2LP_TIME(lbcc) (((lbcc) & 0xff) << 24)
|
||||
#define PHY_LP2HS_TIME(lbcc) (((lbcc) & 0xff) << 16)
|
||||
|
@ -170,12 +186,15 @@
|
|||
#define PHY_UNSHUTDOWNZ BIT(0)
|
||||
|
||||
#define DSI_PHY_IF_CFG 0xa4
|
||||
#define N_LANES(n) ((((n) - 1) & 0x3) << 0)
|
||||
#define PHY_STOP_WAIT_TIME(cycle) (((cycle) & 0xff) << 8)
|
||||
#define N_LANES(n) (((n) - 1) & 0x3)
|
||||
|
||||
#define DSI_PHY_ULPS_CTRL 0xa8
|
||||
#define DSI_PHY_TX_TRIGGERS 0xac
|
||||
|
||||
#define DSI_PHY_STATUS 0xb0
|
||||
#define LOCK BIT(0)
|
||||
#define STOP_STATE_CLK_LANE BIT(2)
|
||||
#define PHY_STOP_STATE_CLK_LANE BIT(2)
|
||||
#define PHY_LOCK BIT(0)
|
||||
|
||||
#define DSI_PHY_TST_CTRL0 0xb4
|
||||
#define PHY_TESTCLK BIT(1)
|
||||
|
@ -187,12 +206,13 @@
|
|||
#define PHY_TESTEN BIT(16)
|
||||
#define PHY_UNTESTEN 0
|
||||
#define PHY_TESTDOUT(n) (((n) & 0xff) << 8)
|
||||
#define PHY_TESTDIN(n) (((n) & 0xff) << 0)
|
||||
#define PHY_TESTDIN(n) ((n) & 0xff)
|
||||
|
||||
#define DSI_INT_ST0 0xbc
|
||||
#define DSI_INT_ST1 0xc0
|
||||
#define DSI_INT_MSK0 0xc4
|
||||
#define DSI_INT_MSK1 0xc8
|
||||
#define DSI_PHY_TMR_RD_CFG 0xf4
|
||||
|
||||
#define PHY_STATUS_TIMEOUT_US 10000
|
||||
#define CMD_PKT_STATUS_TIMEOUT_US 20000
|
||||
|
@ -307,7 +327,7 @@ static void dw_mipi_message_config(struct dw_mipi_dsi *dsi,
|
|||
u32 val = 0;
|
||||
|
||||
if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
|
||||
val |= EN_ACK_RQST;
|
||||
val |= ACK_RQST_EN;
|
||||
if (lpm)
|
||||
val |= CMD_MODE_ALL_LP;
|
||||
|
||||
|
@ -506,8 +526,8 @@ static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
|
|||
* timeout clock division should be computed with the
|
||||
* high speed transmission counter timeout and byte lane...
|
||||
*/
|
||||
dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) |
|
||||
TX_ESC_CLK_DIVIDSION(esc_clk_division));
|
||||
dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) |
|
||||
TX_ESC_CLK_DIVISION(esc_clk_division));
|
||||
}
|
||||
|
||||
static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
|
||||
|
@ -520,7 +540,7 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
|
|||
color = DPI_COLOR_CODING_24BIT;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB666:
|
||||
color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY;
|
||||
color = DPI_COLOR_CODING_18BIT_2 | LOOSELY18_EN;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB666_PACKED:
|
||||
color = DPI_COLOR_CODING_18BIT_1;
|
||||
|
@ -535,7 +555,7 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
|
|||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
val |= HSYNC_ACTIVE_LOW;
|
||||
|
||||
dsi_write(dsi, DSI_DPI_VCID, DPI_VID(dsi->channel));
|
||||
dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel));
|
||||
dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
|
||||
dsi_write(dsi, DSI_DPI_CFG_POL, val);
|
||||
/*
|
||||
|
@ -550,7 +570,7 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
|
|||
|
||||
static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
|
||||
{
|
||||
dsi_write(dsi, DSI_PCKHDL_CFG, EN_CRC_RX | EN_ECC_RX | EN_BTA);
|
||||
dsi_write(dsi, DSI_PCKHDL_CFG, CRC_RX_EN | ECC_RX_EN | BTA_EN);
|
||||
}
|
||||
|
||||
static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
|
||||
|
@ -571,7 +591,7 @@ static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
|
|||
/*
|
||||
* TODO dw drv improvements
|
||||
* compute high speed transmission counter timeout according
|
||||
* to the timeout clock division (TO_CLK_DIVIDSION) and byte lane...
|
||||
* to the timeout clock division (TO_CLK_DIVISION) and byte lane...
|
||||
*/
|
||||
dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
|
||||
/*
|
||||
|
@ -684,13 +704,13 @@ static void dw_mipi_dsi_dphy_enable(struct dw_mipi_dsi *dsi)
|
|||
dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
|
||||
PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
|
||||
|
||||
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
|
||||
val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
|
||||
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, val,
|
||||
val & PHY_LOCK, 1000, PHY_STATUS_TIMEOUT_US);
|
||||
if (ret < 0)
|
||||
DRM_DEBUG_DRIVER("failed to wait phy lock state\n");
|
||||
|
||||
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
|
||||
val, val & STOP_STATE_CLK_LANE, 1000,
|
||||
val, val & PHY_STOP_STATE_CLK_LANE, 1000,
|
||||
PHY_STATUS_TIMEOUT_US);
|
||||
if (ret < 0)
|
||||
DRM_DEBUG_DRIVER("failed to wait phy clk lane stop state\n");
|
||||
|
@ -865,15 +885,14 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
|
|||
* Note that the reset was not defined in the initial device tree, so
|
||||
* we have to be prepared for it not being found.
|
||||
*/
|
||||
apb_rst = devm_reset_control_get(dev, "apb");
|
||||
apb_rst = devm_reset_control_get_optional_exclusive(dev, "apb");
|
||||
if (IS_ERR(apb_rst)) {
|
||||
ret = PTR_ERR(apb_rst);
|
||||
if (ret == -ENOENT) {
|
||||
apb_rst = NULL;
|
||||
} else {
|
||||
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Unable to get reset control: %d\n", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (apb_rst) {
|
||||
|
|
|
@ -163,13 +163,6 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
|
|||
crtc->funcs->atomic_destroy_state(crtc,
|
||||
state->crtcs[i].state);
|
||||
|
||||
if (state->crtcs[i].commit) {
|
||||
kfree(state->crtcs[i].commit->event);
|
||||
state->crtcs[i].commit->event = NULL;
|
||||
drm_crtc_commit_put(state->crtcs[i].commit);
|
||||
}
|
||||
|
||||
state->crtcs[i].commit = NULL;
|
||||
state->crtcs[i].ptr = NULL;
|
||||
state->crtcs[i].state = NULL;
|
||||
}
|
||||
|
@ -199,6 +192,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
|
|||
}
|
||||
state->num_private_objs = 0;
|
||||
|
||||
if (state->fake_commit) {
|
||||
drm_crtc_commit_put(state->fake_commit);
|
||||
state->fake_commit = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_state_default_clear);
|
||||
|
||||
|
@ -2237,7 +2234,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
|
|||
(arg->flags & DRM_MODE_PAGE_FLIP_EVENT))
|
||||
return -EINVAL;
|
||||
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
|
||||
|
||||
state = drm_atomic_state_alloc(dev);
|
||||
if (!state)
|
||||
|
@ -2350,8 +2347,9 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
|
|||
|
||||
if (ret == -EDEADLK) {
|
||||
drm_atomic_state_clear(state);
|
||||
drm_modeset_backoff(&ctx);
|
||||
goto retry;
|
||||
ret = drm_modeset_backoff(&ctx);
|
||||
if (!ret)
|
||||
goto retry;
|
||||
}
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
|
|
|
@ -1262,12 +1262,12 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
|
|||
void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev,
|
||||
struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct drm_crtc_state *unused;
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
struct drm_crtc *crtc;
|
||||
int i;
|
||||
|
||||
for_each_new_crtc_in_state(old_state, crtc, unused, i) {
|
||||
struct drm_crtc_commit *commit = old_state->crtcs[i].commit;
|
||||
for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
|
||||
struct drm_crtc_commit *commit = new_crtc_state->commit;
|
||||
int ret;
|
||||
|
||||
if (!commit)
|
||||
|
@ -1388,35 +1388,31 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
|
|||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_crtc_commit *commit;
|
||||
struct drm_plane *__plane, *plane = NULL;
|
||||
struct drm_plane_state *__plane_state, *plane_state = NULL;
|
||||
struct drm_plane *plane;
|
||||
struct drm_plane_state *old_plane_state, *new_plane_state;
|
||||
const struct drm_plane_helper_funcs *funcs;
|
||||
int i, j, n_planes = 0;
|
||||
int i, n_planes = 0;
|
||||
|
||||
for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
if (drm_atomic_crtc_needs_modeset(crtc_state))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for_each_new_plane_in_state(state, __plane, __plane_state, i) {
|
||||
for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i)
|
||||
n_planes++;
|
||||
plane = __plane;
|
||||
plane_state = __plane_state;
|
||||
}
|
||||
|
||||
/* FIXME: we support only single plane updates for now */
|
||||
if (!plane || n_planes != 1)
|
||||
if (n_planes != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (!plane_state->crtc)
|
||||
if (!new_plane_state->crtc)
|
||||
return -EINVAL;
|
||||
|
||||
funcs = plane->helper_private;
|
||||
if (!funcs->atomic_async_update)
|
||||
return -EINVAL;
|
||||
|
||||
if (plane_state->fence)
|
||||
if (new_plane_state->fence)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
|
@ -1424,31 +1420,11 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
|
|||
* the plane. This prevents our async update's changes from getting
|
||||
* overridden by a previous synchronous update's state.
|
||||
*/
|
||||
for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
if (plane->crtc != crtc)
|
||||
continue;
|
||||
if (old_plane_state->commit &&
|
||||
!try_wait_for_completion(&old_plane_state->commit->hw_done))
|
||||
return -EBUSY;
|
||||
|
||||
spin_lock(&crtc->commit_lock);
|
||||
commit = list_first_entry_or_null(&crtc->commit_list,
|
||||
struct drm_crtc_commit,
|
||||
commit_entry);
|
||||
if (!commit) {
|
||||
spin_unlock(&crtc->commit_lock);
|
||||
continue;
|
||||
}
|
||||
spin_unlock(&crtc->commit_lock);
|
||||
|
||||
if (!crtc->state->state)
|
||||
continue;
|
||||
|
||||
for_each_plane_in_state(crtc->state->state, __plane,
|
||||
__plane_state, j) {
|
||||
if (__plane == plane)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return funcs->atomic_async_check(plane, plane_state);
|
||||
return funcs->atomic_async_check(plane, new_plane_state);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_async_check);
|
||||
|
||||
|
@ -1633,8 +1609,7 @@ static int stall_checks(struct drm_crtc *crtc, bool nonblock)
|
|||
return -EBUSY;
|
||||
}
|
||||
} else if (i == 1) {
|
||||
stall_commit = commit;
|
||||
drm_crtc_commit_get(stall_commit);
|
||||
stall_commit = drm_crtc_commit_get(commit);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1668,6 +1643,38 @@ static void release_crtc_commit(struct completion *completion)
|
|||
drm_crtc_commit_put(commit);
|
||||
}
|
||||
|
||||
static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc *crtc)
|
||||
{
|
||||
init_completion(&commit->flip_done);
|
||||
init_completion(&commit->hw_done);
|
||||
init_completion(&commit->cleanup_done);
|
||||
INIT_LIST_HEAD(&commit->commit_entry);
|
||||
kref_init(&commit->ref);
|
||||
commit->crtc = crtc;
|
||||
}
|
||||
|
||||
static struct drm_crtc_commit *
|
||||
crtc_or_fake_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
|
||||
{
|
||||
if (crtc) {
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
|
||||
new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
|
||||
return new_crtc_state->commit;
|
||||
}
|
||||
|
||||
if (!state->fake_commit) {
|
||||
state->fake_commit = kzalloc(sizeof(*state->fake_commit), GFP_KERNEL);
|
||||
if (!state->fake_commit)
|
||||
return NULL;
|
||||
|
||||
init_commit(state->fake_commit, NULL);
|
||||
}
|
||||
|
||||
return state->fake_commit;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_atomic_helper_setup_commit - setup possibly nonblocking commit
|
||||
* @state: new modeset state to be committed
|
||||
|
@ -1716,6 +1723,10 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
|
|||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
|
||||
struct drm_connector *conn;
|
||||
struct drm_connector_state *old_conn_state, *new_conn_state;
|
||||
struct drm_plane *plane;
|
||||
struct drm_plane_state *old_plane_state, *new_plane_state;
|
||||
struct drm_crtc_commit *commit;
|
||||
int i, ret;
|
||||
|
||||
|
@ -1724,14 +1735,9 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
|
|||
if (!commit)
|
||||
return -ENOMEM;
|
||||
|
||||
init_completion(&commit->flip_done);
|
||||
init_completion(&commit->hw_done);
|
||||
init_completion(&commit->cleanup_done);
|
||||
INIT_LIST_HEAD(&commit->commit_entry);
|
||||
kref_init(&commit->ref);
|
||||
commit->crtc = crtc;
|
||||
init_commit(commit, crtc);
|
||||
|
||||
state->crtcs[i].commit = commit;
|
||||
new_crtc_state->commit = commit;
|
||||
|
||||
ret = stall_checks(crtc, nonblock);
|
||||
if (ret)
|
||||
|
@ -1765,26 +1771,47 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
|
|||
drm_crtc_commit_get(commit);
|
||||
}
|
||||
|
||||
for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) {
|
||||
/* commit tracked through new_crtc_state->commit, no need to do it explicitly */
|
||||
if (new_conn_state->crtc)
|
||||
continue;
|
||||
|
||||
/* Userspace is not allowed to get ahead of the previous
|
||||
* commit with nonblocking ones. */
|
||||
if (nonblock && old_conn_state->commit &&
|
||||
!try_wait_for_completion(&old_conn_state->commit->flip_done))
|
||||
return -EBUSY;
|
||||
|
||||
commit = crtc_or_fake_commit(state, old_conn_state->crtc);
|
||||
if (!commit)
|
||||
return -ENOMEM;
|
||||
|
||||
new_conn_state->commit = drm_crtc_commit_get(commit);
|
||||
}
|
||||
|
||||
for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
|
||||
/*
|
||||
* Unlike connectors, always track planes explicitly for
|
||||
* async pageflip support.
|
||||
*/
|
||||
|
||||
/* Userspace is not allowed to get ahead of the previous
|
||||
* commit with nonblocking ones. */
|
||||
if (nonblock && old_plane_state->commit &&
|
||||
!try_wait_for_completion(&old_plane_state->commit->flip_done))
|
||||
return -EBUSY;
|
||||
|
||||
commit = crtc_or_fake_commit(state, old_plane_state->crtc);
|
||||
if (!commit)
|
||||
return -ENOMEM;
|
||||
|
||||
new_plane_state->commit = drm_crtc_commit_get(commit);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
|
||||
|
||||
|
||||
static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_crtc_commit *commit;
|
||||
int i = 0;
|
||||
|
||||
list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
|
||||
/* skip the first entry, that's the current commit */
|
||||
if (i == 1)
|
||||
return commit;
|
||||
i++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits
|
||||
* @old_state: atomic state object with old state structures
|
||||
|
@ -1800,17 +1827,17 @@ static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc)
|
|||
void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
struct drm_crtc_state *old_crtc_state;
|
||||
struct drm_plane *plane;
|
||||
struct drm_plane_state *old_plane_state;
|
||||
struct drm_connector *conn;
|
||||
struct drm_connector_state *old_conn_state;
|
||||
struct drm_crtc_commit *commit;
|
||||
int i;
|
||||
long ret;
|
||||
|
||||
for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
|
||||
spin_lock(&crtc->commit_lock);
|
||||
commit = preceeding_commit(crtc);
|
||||
if (commit)
|
||||
drm_crtc_commit_get(commit);
|
||||
spin_unlock(&crtc->commit_lock);
|
||||
for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
|
||||
commit = old_crtc_state->commit;
|
||||
|
||||
if (!commit)
|
||||
continue;
|
||||
|
@ -1828,8 +1855,48 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
|
|||
if (ret == 0)
|
||||
DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
|
||||
crtc->base.id, crtc->name);
|
||||
}
|
||||
|
||||
drm_crtc_commit_put(commit);
|
||||
for_each_old_connector_in_state(old_state, conn, old_conn_state, i) {
|
||||
commit = old_conn_state->commit;
|
||||
|
||||
if (!commit)
|
||||
continue;
|
||||
|
||||
ret = wait_for_completion_timeout(&commit->hw_done,
|
||||
10*HZ);
|
||||
if (ret == 0)
|
||||
DRM_ERROR("[CONNECTOR:%d:%s] hw_done timed out\n",
|
||||
conn->base.id, conn->name);
|
||||
|
||||
/* Currently no support for overwriting flips, hence
|
||||
* stall for previous one to execute completely. */
|
||||
ret = wait_for_completion_timeout(&commit->flip_done,
|
||||
10*HZ);
|
||||
if (ret == 0)
|
||||
DRM_ERROR("[CONNECTOR:%d:%s] flip_done timed out\n",
|
||||
conn->base.id, conn->name);
|
||||
}
|
||||
|
||||
for_each_old_plane_in_state(old_state, plane, old_plane_state, i) {
|
||||
commit = old_plane_state->commit;
|
||||
|
||||
if (!commit)
|
||||
continue;
|
||||
|
||||
ret = wait_for_completion_timeout(&commit->hw_done,
|
||||
10*HZ);
|
||||
if (ret == 0)
|
||||
DRM_ERROR("[PLANE:%d:%s] hw_done timed out\n",
|
||||
plane->base.id, plane->name);
|
||||
|
||||
/* Currently no support for overwriting flips, hence
|
||||
* stall for previous one to execute completely. */
|
||||
ret = wait_for_completion_timeout(&commit->flip_done,
|
||||
10*HZ);
|
||||
if (ret == 0)
|
||||
DRM_ERROR("[PLANE:%d:%s] flip_done timed out\n",
|
||||
plane->base.id, plane->name);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
|
||||
|
@ -1852,19 +1919,34 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
|
|||
void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
|
||||
struct drm_crtc_commit *commit;
|
||||
int i;
|
||||
|
||||
for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
|
||||
commit = old_state->crtcs[i].commit;
|
||||
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
|
||||
commit = new_crtc_state->commit;
|
||||
if (!commit)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* copy new_crtc_state->commit to old_crtc_state->commit,
|
||||
* it's unsafe to touch new_crtc_state after hw_done,
|
||||
* but we still need to do so in cleanup_done().
|
||||
*/
|
||||
if (old_crtc_state->commit)
|
||||
drm_crtc_commit_put(old_crtc_state->commit);
|
||||
|
||||
old_crtc_state->commit = drm_crtc_commit_get(commit);
|
||||
|
||||
/* backend must have consumed any event by now */
|
||||
WARN_ON(new_crtc_state->event);
|
||||
complete_all(&commit->hw_done);
|
||||
}
|
||||
|
||||
if (old_state->fake_commit) {
|
||||
complete_all(&old_state->fake_commit->hw_done);
|
||||
complete_all(&old_state->fake_commit->flip_done);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
|
||||
|
||||
|
@ -1882,39 +1964,25 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
|
|||
void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
struct drm_crtc_state *old_crtc_state;
|
||||
struct drm_crtc_commit *commit;
|
||||
int i;
|
||||
long ret;
|
||||
|
||||
for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
|
||||
commit = old_state->crtcs[i].commit;
|
||||
for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
|
||||
commit = old_crtc_state->commit;
|
||||
if (WARN_ON(!commit))
|
||||
continue;
|
||||
|
||||
complete_all(&commit->cleanup_done);
|
||||
WARN_ON(!try_wait_for_completion(&commit->hw_done));
|
||||
|
||||
/* commit_list borrows our reference, need to remove before we
|
||||
* clean up our drm_atomic_state. But only after it actually
|
||||
* completed, otherwise subsequent commits won't stall properly. */
|
||||
if (try_wait_for_completion(&commit->flip_done))
|
||||
goto del_commit;
|
||||
|
||||
/* We must wait for the vblank event to signal our completion
|
||||
* before releasing our reference, since the vblank work does
|
||||
* not hold a reference of its own. */
|
||||
ret = wait_for_completion_timeout(&commit->flip_done,
|
||||
10*HZ);
|
||||
if (ret == 0)
|
||||
DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
|
||||
crtc->base.id, crtc->name);
|
||||
|
||||
del_commit:
|
||||
spin_lock(&crtc->commit_lock);
|
||||
list_del(&commit->commit_entry);
|
||||
spin_unlock(&crtc->commit_lock);
|
||||
}
|
||||
|
||||
if (old_state->fake_commit)
|
||||
complete_all(&old_state->fake_commit->cleanup_done);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
|
||||
|
||||
|
@ -2294,20 +2362,44 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
|||
struct drm_private_state *old_obj_state, *new_obj_state;
|
||||
|
||||
if (stall) {
|
||||
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
|
||||
spin_lock(&crtc->commit_lock);
|
||||
commit = list_first_entry_or_null(&crtc->commit_list,
|
||||
struct drm_crtc_commit, commit_entry);
|
||||
if (commit)
|
||||
drm_crtc_commit_get(commit);
|
||||
spin_unlock(&crtc->commit_lock);
|
||||
/*
|
||||
* We have to stall for hw_done here before
|
||||
* drm_atomic_helper_wait_for_dependencies() because flip
|
||||
* depth > 1 is not yet supported by all drivers. As long as
|
||||
* obj->state is directly dereferenced anywhere in the drivers
|
||||
* atomic_commit_tail function, then it's unsafe to swap state
|
||||
* before drm_atomic_helper_commit_hw_done() is called.
|
||||
*/
|
||||
|
||||
for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
|
||||
commit = old_crtc_state->commit;
|
||||
|
||||
if (!commit)
|
||||
continue;
|
||||
|
||||
ret = wait_for_completion_interruptible(&commit->hw_done);
|
||||
drm_crtc_commit_put(commit);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_old_connector_in_state(state, connector, old_conn_state, i) {
|
||||
commit = old_conn_state->commit;
|
||||
|
||||
if (!commit)
|
||||
continue;
|
||||
|
||||
ret = wait_for_completion_interruptible(&commit->hw_done);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_old_plane_in_state(state, plane, old_plane_state, i) {
|
||||
commit = old_plane_state->commit;
|
||||
|
||||
if (!commit)
|
||||
continue;
|
||||
|
||||
ret = wait_for_completion_interruptible(&commit->hw_done);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -2332,13 +2424,13 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
|||
state->crtcs[i].state = old_crtc_state;
|
||||
crtc->state = new_crtc_state;
|
||||
|
||||
if (state->crtcs[i].commit) {
|
||||
if (new_crtc_state->commit) {
|
||||
spin_lock(&crtc->commit_lock);
|
||||
list_add(&state->crtcs[i].commit->commit_entry,
|
||||
list_add(&new_crtc_state->commit->commit_entry,
|
||||
&crtc->commit_list);
|
||||
spin_unlock(&crtc->commit_lock);
|
||||
|
||||
state->crtcs[i].commit->event = NULL;
|
||||
new_crtc_state->commit->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3186,6 +3278,7 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
|
|||
state->connectors_changed = false;
|
||||
state->color_mgmt_changed = false;
|
||||
state->zpos_changed = false;
|
||||
state->commit = NULL;
|
||||
state->event = NULL;
|
||||
state->pageflip_flags = 0;
|
||||
}
|
||||
|
@ -3224,6 +3317,12 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
|
|||
*/
|
||||
void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
|
||||
{
|
||||
if (state->commit) {
|
||||
kfree(state->commit->event);
|
||||
state->commit->event = NULL;
|
||||
drm_crtc_commit_put(state->commit);
|
||||
}
|
||||
|
||||
drm_property_blob_put(state->mode_blob);
|
||||
drm_property_blob_put(state->degamma_lut);
|
||||
drm_property_blob_put(state->ctm);
|
||||
|
@ -3286,6 +3385,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
|
|||
drm_framebuffer_get(state->fb);
|
||||
|
||||
state->fence = NULL;
|
||||
state->commit = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
|
||||
|
||||
|
@ -3327,6 +3427,9 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state)
|
|||
|
||||
if (state->fence)
|
||||
dma_fence_put(state->fence);
|
||||
|
||||
if (state->commit)
|
||||
drm_crtc_commit_put(state->commit);
|
||||
}
|
||||
EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
|
||||
|
||||
|
@ -3405,6 +3508,7 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
|
|||
memcpy(state, connector->state, sizeof(*state));
|
||||
if (state->crtc)
|
||||
drm_connector_get(connector);
|
||||
state->commit = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
|
||||
|
||||
|
@ -3531,6 +3635,9 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
|
|||
{
|
||||
if (state->crtc)
|
||||
drm_connector_put(state->connector);
|
||||
|
||||
if (state->commit)
|
||||
drm_crtc_commit_put(state->commit);
|
||||
}
|
||||
EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
|
||||
|
||||
|
|
|
@ -67,17 +67,12 @@ static LIST_HEAD(bridge_list);
|
|||
* drm_bridge_add - add the given bridge to the global bridge list
|
||||
*
|
||||
* @bridge: bridge control structure
|
||||
*
|
||||
* RETURNS:
|
||||
* Unconditionally returns Zero.
|
||||
*/
|
||||
int drm_bridge_add(struct drm_bridge *bridge)
|
||||
void drm_bridge_add(struct drm_bridge *bridge)
|
||||
{
|
||||
mutex_lock(&bridge_lock);
|
||||
list_add_tail(&bridge->list, &bridge_list);
|
||||
mutex_unlock(&bridge_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_bridge_add);
|
||||
|
||||
|
|
|
@ -615,7 +615,6 @@ static const struct drm_prop_enum_list drm_link_status_enum_list[] = {
|
|||
{ DRM_MODE_LINK_STATUS_GOOD, "Good" },
|
||||
{ DRM_MODE_LINK_STATUS_BAD, "Bad" },
|
||||
};
|
||||
DRM_ENUM_NAME_FN(drm_get_link_status_name, drm_link_status_enum_list)
|
||||
|
||||
/**
|
||||
* drm_display_info_set_bus_formats - set the supported bus formats
|
||||
|
|
|
@ -577,7 +577,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
|||
DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
|
||||
|
||||
mutex_lock(&crtc->dev->mode_config.mutex);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
|
||||
retry:
|
||||
ret = drm_modeset_lock_all_ctx(crtc->dev, &ctx);
|
||||
if (ret)
|
||||
|
@ -717,8 +717,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
|||
kfree(connector_set);
|
||||
drm_mode_destroy(dev, mode);
|
||||
if (ret == -EDEADLK) {
|
||||
drm_modeset_backoff(&ctx);
|
||||
goto retry;
|
||||
ret = drm_modeset_backoff(&ctx);
|
||||
if (!ret)
|
||||
goto retry;
|
||||
}
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
|
|
|
@ -155,7 +155,7 @@ static int crtc_crc_open(struct inode *inode, struct file *filep)
|
|||
int ret = 0;
|
||||
|
||||
if (drm_drv_uses_atomic_modeset(crtc->dev)) {
|
||||
ret = drm_modeset_lock_interruptible(&crtc->mutex, NULL);
|
||||
ret = drm_modeset_lock_single_interruptible(&crtc->mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -294,6 +294,12 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
|
|||
memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
|
||||
idx += req->u.i2c_write.num_bytes;
|
||||
break;
|
||||
|
||||
case DP_POWER_DOWN_PHY:
|
||||
case DP_POWER_UP_PHY:
|
||||
buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
|
||||
idx++;
|
||||
break;
|
||||
}
|
||||
raw->cur_len = idx;
|
||||
}
|
||||
|
@ -538,6 +544,21 @@ static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_r
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool drm_dp_sideband_parse_power_updown_phy_ack(struct drm_dp_sideband_msg_rx *raw,
|
||||
struct drm_dp_sideband_msg_reply_body *repmsg)
|
||||
{
|
||||
int idx = 1;
|
||||
|
||||
repmsg->u.port_number.port_number = (raw->msg[idx] >> 4) & 0xf;
|
||||
idx++;
|
||||
if (idx > raw->curlen) {
|
||||
DRM_DEBUG_KMS("power up/down phy parse length fail %d %d\n",
|
||||
idx, raw->curlen);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
|
||||
struct drm_dp_sideband_msg_reply_body *msg)
|
||||
{
|
||||
|
@ -567,6 +588,9 @@ static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
|
|||
return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
|
||||
case DP_ALLOCATE_PAYLOAD:
|
||||
return drm_dp_sideband_parse_allocate_payload_ack(raw, msg);
|
||||
case DP_POWER_DOWN_PHY:
|
||||
case DP_POWER_UP_PHY:
|
||||
return drm_dp_sideband_parse_power_updown_phy_ack(raw, msg);
|
||||
default:
|
||||
DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type);
|
||||
return false;
|
||||
|
@ -693,6 +717,22 @@ static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_n
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int build_power_updown_phy(struct drm_dp_sideband_msg_tx *msg,
|
||||
int port_num, bool power_up)
|
||||
{
|
||||
struct drm_dp_sideband_msg_req_body req;
|
||||
|
||||
if (power_up)
|
||||
req.req_type = DP_POWER_UP_PHY;
|
||||
else
|
||||
req.req_type = DP_POWER_DOWN_PHY;
|
||||
|
||||
req.u.port_num.port_number = port_num;
|
||||
drm_dp_encode_sideband_req(&req, msg);
|
||||
msg->path_msg = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_vcpi *vcpi)
|
||||
{
|
||||
|
@ -1724,6 +1764,40 @@ static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port, bool power_up)
|
||||
{
|
||||
struct drm_dp_sideband_msg_tx *txmsg;
|
||||
int len, ret;
|
||||
|
||||
port = drm_dp_get_validated_port_ref(mgr, port);
|
||||
if (!port)
|
||||
return -EINVAL;
|
||||
|
||||
txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
|
||||
if (!txmsg) {
|
||||
drm_dp_put_port(port);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
txmsg->dst = port->parent;
|
||||
len = build_power_updown_phy(txmsg, port->port_num, power_up);
|
||||
drm_dp_queue_down_tx(mgr, txmsg);
|
||||
|
||||
ret = drm_dp_mst_wait_tx_reply(port->parent, txmsg);
|
||||
if (ret > 0) {
|
||||
if (txmsg->reply.reply_type == 1)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = 0;
|
||||
}
|
||||
kfree(txmsg);
|
||||
drm_dp_put_port(port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_send_power_updown_phy);
|
||||
|
||||
static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
|
||||
int id,
|
||||
struct drm_dp_payload *payload)
|
||||
|
|
|
@ -1533,6 +1533,10 @@ static void connector_bad_edid(struct drm_connector *connector,
|
|||
* level, drivers must make all reasonable efforts to expose it as an I2C
|
||||
* adapter and use drm_get_edid() instead of abusing this function.
|
||||
*
|
||||
* The EDID may be overridden using debugfs override_edid or firmare EDID
|
||||
* (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority
|
||||
* order. Having either of them bypasses actual EDID reads.
|
||||
*
|
||||
* Return: Pointer to valid EDID or NULL if we couldn't find any.
|
||||
*/
|
||||
struct edid *drm_do_get_edid(struct drm_connector *connector,
|
||||
|
@ -1542,6 +1546,17 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
|
|||
{
|
||||
int i, j = 0, valid_extensions = 0;
|
||||
u8 *edid, *new;
|
||||
struct edid *override = NULL;
|
||||
|
||||
if (connector->override_edid)
|
||||
override = drm_edid_duplicate((const struct edid *)
|
||||
connector->edid_blob_ptr->data);
|
||||
|
||||
if (!override)
|
||||
override = drm_load_edid_firmware(connector);
|
||||
|
||||
if (!IS_ERR_OR_NULL(override))
|
||||
return override;
|
||||
|
||||
if ((edid = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL)
|
||||
return NULL;
|
||||
|
|
|
@ -31,6 +31,22 @@ module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
|
|||
MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
|
||||
"from built-in data or /lib/firmware instead. ");
|
||||
|
||||
/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
|
||||
int __drm_set_edid_firmware_path(const char *path)
|
||||
{
|
||||
scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(__drm_set_edid_firmware_path);
|
||||
|
||||
/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
|
||||
int __drm_get_edid_firmware_path(char *buf, size_t bufsize)
|
||||
{
|
||||
return scnprintf(buf, bufsize, "%s", edid_firmware);
|
||||
}
|
||||
EXPORT_SYMBOL(__drm_get_edid_firmware_path);
|
||||
|
||||
#define GENERIC_EDIDS 6
|
||||
static const char * const generic_edid_name[GENERIC_EDIDS] = {
|
||||
"edid/800x600.bin",
|
||||
|
|
|
@ -910,6 +910,9 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
|
|||
if (!drm_fbdev_emulation || !fb_helper)
|
||||
return;
|
||||
|
||||
cancel_work_sync(&fb_helper->resume_work);
|
||||
cancel_work_sync(&fb_helper->dirty_work);
|
||||
|
||||
info = fb_helper->fbdev;
|
||||
if (info) {
|
||||
if (info->cmap.len)
|
||||
|
@ -918,9 +921,6 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
|
|||
}
|
||||
fb_helper->fbdev = NULL;
|
||||
|
||||
cancel_work_sync(&fb_helper->resume_work);
|
||||
cancel_work_sync(&fb_helper->dirty_work);
|
||||
|
||||
mutex_lock(&kernel_fb_helper_lock);
|
||||
if (!list_empty(&fb_helper->kernel_fb_list)) {
|
||||
list_del(&fb_helper->kernel_fb_list);
|
||||
|
|
|
@ -334,6 +334,12 @@ int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
|
|||
if (!obj)
|
||||
return -ENOENT;
|
||||
|
||||
/* Don't allow imported objects to be mapped */
|
||||
if (obj->import_attach) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = drm_gem_create_mmap_offset(obj);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
|
|
@ -154,7 +154,7 @@ drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
|
|||
|
||||
objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
|
||||
if (!objs[i]) {
|
||||
DRM_DEV_ERROR(dev->dev, "Failed to lookup GEM\n");
|
||||
DRM_DEBUG_KMS("Failed to lookup GEM object\n");
|
||||
ret = -ENOENT;
|
||||
goto err_gem_object_put;
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane,
|
|||
struct dma_buf *dma_buf;
|
||||
struct dma_fence *fence;
|
||||
|
||||
if ((plane->state->fb == state->fb) || !state->fb)
|
||||
if (plane->state->fb == state->fb || !state->fb)
|
||||
return 0;
|
||||
|
||||
dma_buf = drm_gem_fb_get_obj(state->fb, 0)->dma_buf;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "drm_crtc_helper_internal.h"
|
||||
|
||||
|
@ -33,6 +34,33 @@ MODULE_AUTHOR("David Airlie, Jesse Barnes");
|
|||
MODULE_DESCRIPTION("DRM KMS helper");
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_LOAD_EDID_FIRMWARE)
|
||||
|
||||
/* Backward compatibility for drm_kms_helper.edid_firmware */
|
||||
static int edid_firmware_set(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
DRM_NOTE("drm_kms_firmware.edid_firmware is deprecated, please use drm.edid_firmware intead.\n");
|
||||
|
||||
return __drm_set_edid_firmware_path(val);
|
||||
}
|
||||
|
||||
static int edid_firmware_get(char *buffer, const struct kernel_param *kp)
|
||||
{
|
||||
return __drm_get_edid_firmware_path(buffer, PAGE_SIZE);
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops edid_firmware_ops = {
|
||||
.set = edid_firmware_set,
|
||||
.get = edid_firmware_get,
|
||||
};
|
||||
|
||||
module_param_cb(edid_firmware, &edid_firmware_ops, NULL, 0644);
|
||||
__MODULE_PARM_TYPE(edid_firmware, "charp");
|
||||
MODULE_PARM_DESC(edid_firmware,
|
||||
"DEPRECATED. Use drm.edid_firmware module parameter instead.");
|
||||
|
||||
#endif
|
||||
|
||||
static int __init drm_kms_helper_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
|
|
@ -247,8 +247,9 @@ int drm_object_property_set_value(struct drm_mode_object *obj,
|
|||
}
|
||||
EXPORT_SYMBOL(drm_object_property_set_value);
|
||||
|
||||
int __drm_object_property_get_value(struct drm_mode_object *obj,
|
||||
struct drm_property *property, uint64_t *val)
|
||||
static int __drm_object_property_get_value(struct drm_mode_object *obj,
|
||||
struct drm_property *property,
|
||||
uint64_t *val)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
|
@ -39,23 +39,28 @@
|
|||
*
|
||||
* The basic usage pattern is to::
|
||||
*
|
||||
* drm_modeset_acquire_init(&ctx)
|
||||
* drm_modeset_acquire_init(ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
|
||||
* retry:
|
||||
* foreach (lock in random_ordered_set_of_locks) {
|
||||
* ret = drm_modeset_lock(lock, &ctx)
|
||||
* ret = drm_modeset_lock(lock, ctx)
|
||||
* if (ret == -EDEADLK) {
|
||||
* drm_modeset_backoff(&ctx);
|
||||
* goto retry;
|
||||
* ret = drm_modeset_backoff(ctx);
|
||||
* if (!ret)
|
||||
* goto retry;
|
||||
* }
|
||||
* if (ret)
|
||||
* goto out;
|
||||
* }
|
||||
* ... do stuff ...
|
||||
* drm_modeset_drop_locks(&ctx);
|
||||
* drm_modeset_acquire_fini(&ctx);
|
||||
* out:
|
||||
* drm_modeset_drop_locks(ctx);
|
||||
* drm_modeset_acquire_fini(ctx);
|
||||
*
|
||||
* If all that is needed is a single modeset lock, then the &struct
|
||||
* drm_modeset_acquire_ctx is not needed and the locking can be simplified
|
||||
* by passing a NULL instead of ctx in the drm_modeset_lock()
|
||||
* call and, when done, by calling drm_modeset_unlock().
|
||||
* by passing a NULL instead of ctx in the drm_modeset_lock() call or
|
||||
* calling drm_modeset_lock_single_interruptible(). To unlock afterwards
|
||||
* call drm_modeset_unlock().
|
||||
*
|
||||
* On top of these per-object locks using &ww_mutex there's also an overall
|
||||
* &drm_mode_config.mutex, for protecting everything else. Mostly this means
|
||||
|
@ -178,7 +183,11 @@ EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
|
|||
/**
|
||||
* drm_modeset_acquire_init - initialize acquire context
|
||||
* @ctx: the acquire context
|
||||
* @flags: for future
|
||||
* @flags: 0 or %DRM_MODESET_ACQUIRE_INTERRUPTIBLE
|
||||
*
|
||||
* When passing %DRM_MODESET_ACQUIRE_INTERRUPTIBLE to @flags,
|
||||
* all calls to drm_modeset_lock() will perform an interruptible
|
||||
* wait.
|
||||
*/
|
||||
void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
|
||||
uint32_t flags)
|
||||
|
@ -186,6 +195,9 @@ void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
|
|||
memset(ctx, 0, sizeof(*ctx));
|
||||
ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
|
||||
INIT_LIST_HEAD(&ctx->locked);
|
||||
|
||||
if (flags & DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
|
||||
ctx->interruptible = true;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_modeset_acquire_init);
|
||||
|
||||
|
@ -261,8 +273,19 @@ static inline int modeset_lock(struct drm_modeset_lock *lock,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
|
||||
bool interruptible)
|
||||
/**
|
||||
* drm_modeset_backoff - deadlock avoidance backoff
|
||||
* @ctx: the acquire context
|
||||
*
|
||||
* If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
|
||||
* you must call this function to drop all currently held locks and
|
||||
* block until the contended lock becomes available.
|
||||
*
|
||||
* This function returns 0 on success, or -ERESTARTSYS if this context
|
||||
* is initialized with %DRM_MODESET_ACQUIRE_INTERRUPTIBLE and the
|
||||
* wait has been interrupted.
|
||||
*/
|
||||
int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
struct drm_modeset_lock *contended = ctx->contended;
|
||||
|
||||
|
@ -273,35 +296,10 @@ static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
|
|||
|
||||
drm_modeset_drop_locks(ctx);
|
||||
|
||||
return modeset_lock(contended, ctx, interruptible, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_modeset_backoff - deadlock avoidance backoff
|
||||
* @ctx: the acquire context
|
||||
*
|
||||
* If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
|
||||
* you must call this function to drop all currently held locks and
|
||||
* block until the contended lock becomes available.
|
||||
*/
|
||||
void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
modeset_backoff(ctx, false);
|
||||
return modeset_lock(contended, ctx, ctx->interruptible, true);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_modeset_backoff);
|
||||
|
||||
/**
|
||||
* drm_modeset_backoff_interruptible - deadlock avoidance backoff
|
||||
* @ctx: the acquire context
|
||||
*
|
||||
* Interruptible version of drm_modeset_backoff()
|
||||
*/
|
||||
int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
return modeset_backoff(ctx, true);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
|
||||
|
||||
/**
|
||||
* drm_modeset_lock_init - initialize lock
|
||||
* @lock: lock to init
|
||||
|
@ -324,14 +322,18 @@ EXPORT_SYMBOL(drm_modeset_lock_init);
|
|||
* deadlock scenario has been detected and it is an error to attempt
|
||||
* to take any more locks without first calling drm_modeset_backoff().
|
||||
*
|
||||
* If the @ctx is not NULL and initialized with
|
||||
* %DRM_MODESET_ACQUIRE_INTERRUPTIBLE, this function will fail with
|
||||
* -ERESTARTSYS when interrupted.
|
||||
*
|
||||
* If @ctx is NULL then the function call behaves like a normal,
|
||||
* non-nesting mutex_lock() call.
|
||||
* uninterruptible non-nesting mutex_lock() call.
|
||||
*/
|
||||
int drm_modeset_lock(struct drm_modeset_lock *lock,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
if (ctx)
|
||||
return modeset_lock(lock, ctx, false, false);
|
||||
return modeset_lock(lock, ctx, ctx->interruptible, false);
|
||||
|
||||
ww_mutex_lock(&lock->mutex, NULL);
|
||||
return 0;
|
||||
|
@ -339,21 +341,19 @@ int drm_modeset_lock(struct drm_modeset_lock *lock,
|
|||
EXPORT_SYMBOL(drm_modeset_lock);
|
||||
|
||||
/**
|
||||
* drm_modeset_lock_interruptible - take modeset lock
|
||||
* drm_modeset_lock_single_interruptible - take a single modeset lock
|
||||
* @lock: lock to take
|
||||
* @ctx: acquire ctx
|
||||
*
|
||||
* Interruptible version of drm_modeset_lock()
|
||||
* This function behaves as drm_modeset_lock() with a NULL context,
|
||||
* but performs interruptible waits.
|
||||
*
|
||||
* This function returns 0 on success, or -ERESTARTSYS when interrupted.
|
||||
*/
|
||||
int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
int drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock)
|
||||
{
|
||||
if (ctx)
|
||||
return modeset_lock(lock, ctx, true, false);
|
||||
|
||||
return ww_mutex_lock_interruptible(&lock->mutex, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_modeset_lock_interruptible);
|
||||
EXPORT_SYMBOL(drm_modeset_lock_single_interruptible);
|
||||
|
||||
/**
|
||||
* drm_modeset_unlock - drop modeset lock
|
||||
|
|
|
@ -667,7 +667,7 @@ static int setplane_internal(struct drm_plane *plane,
|
|||
struct drm_modeset_acquire_ctx ctx;
|
||||
int ret;
|
||||
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
|
||||
retry:
|
||||
ret = drm_modeset_lock_all_ctx(plane->dev, &ctx);
|
||||
if (ret)
|
||||
|
@ -678,8 +678,9 @@ static int setplane_internal(struct drm_plane *plane,
|
|||
|
||||
fail:
|
||||
if (ret == -EDEADLK) {
|
||||
drm_modeset_backoff(&ctx);
|
||||
goto retry;
|
||||
ret = drm_modeset_backoff(&ctx);
|
||||
if (!ret)
|
||||
goto retry;
|
||||
}
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
|
@ -834,7 +835,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
|
||||
retry:
|
||||
ret = drm_modeset_lock(&crtc->mutex, &ctx);
|
||||
if (ret)
|
||||
|
@ -876,8 +877,9 @@ static int drm_mode_cursor_common(struct drm_device *dev,
|
|||
}
|
||||
out:
|
||||
if (ret == -EDEADLK) {
|
||||
drm_modeset_backoff(&ctx);
|
||||
goto retry;
|
||||
ret = drm_modeset_backoff(&ctx);
|
||||
if (!ret)
|
||||
goto retry;
|
||||
}
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
|
@ -985,7 +987,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
|
||||
retry:
|
||||
ret = drm_modeset_lock(&crtc->mutex, &ctx);
|
||||
if (ret)
|
||||
|
@ -1074,8 +1076,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
|||
crtc->primary->old_fb = NULL;
|
||||
|
||||
if (ret == -EDEADLK) {
|
||||
drm_modeset_backoff(&ctx);
|
||||
goto retry;
|
||||
ret = drm_modeset_backoff(&ctx);
|
||||
if (!ret)
|
||||
goto retry;
|
||||
}
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
|
|
|
@ -353,8 +353,6 @@ EXPORT_SYMBOL(drm_helper_probe_detect);
|
|||
* drm_mode_probed_add(). New modes start their life with status as OK.
|
||||
* Modes are added from a single source using the following priority order.
|
||||
*
|
||||
* - debugfs 'override_edid' (used for testing only)
|
||||
* - firmware EDID (drm_load_edid_firmware())
|
||||
* - &drm_connector_helper_funcs.get_modes vfunc
|
||||
* - if the connector status is connector_status_connected, standard
|
||||
* VESA DMT modes up to 1024x768 are automatically added
|
||||
|
@ -483,22 +481,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
|
|||
goto prune;
|
||||
}
|
||||
|
||||
if (connector->override_edid) {
|
||||
struct edid *edid = (struct edid *) connector->edid_blob_ptr->data;
|
||||
|
||||
count = drm_add_edid_modes(connector, edid);
|
||||
drm_edid_to_eld(connector, edid);
|
||||
} else {
|
||||
struct edid *edid = drm_load_edid_firmware(connector);
|
||||
if (!IS_ERR_OR_NULL(edid)) {
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
count = drm_add_edid_modes(connector, edid);
|
||||
drm_edid_to_eld(connector, edid);
|
||||
kfree(edid);
|
||||
}
|
||||
if (count == 0)
|
||||
count = (*connector_funcs->get_modes)(connector);
|
||||
}
|
||||
count = (*connector_funcs->get_modes)(connector);
|
||||
|
||||
if (count == 0 && connector->status == connector_status_connected)
|
||||
count = drm_add_modes_noedid(connector, 1024, 768);
|
||||
|
|
|
@ -134,7 +134,6 @@ EXPORT_SYMBOL(drm_scdc_write);
|
|||
* Returns:
|
||||
* True if the scrambling is enabled, false otherwise.
|
||||
*/
|
||||
|
||||
bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter)
|
||||
{
|
||||
u8 status;
|
||||
|
@ -142,7 +141,7 @@ bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter)
|
|||
|
||||
ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to read scrambling status, error %d\n", ret);
|
||||
DRM_ERROR("Failed to read scrambling status: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -162,7 +161,6 @@ EXPORT_SYMBOL(drm_scdc_get_scrambling_status);
|
|||
* Returns:
|
||||
* True if scrambling is set/reset successfully, false otherwise.
|
||||
*/
|
||||
|
||||
bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
|
||||
{
|
||||
u8 config;
|
||||
|
@ -170,7 +168,7 @@ bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
|
|||
|
||||
ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to read tmds config, err=%d\n", ret);
|
||||
DRM_ERROR("Failed to read TMDS config: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -181,7 +179,7 @@ bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
|
|||
|
||||
ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to enable scrambling, error %d\n", ret);
|
||||
DRM_ERROR("Failed to enable scrambling: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -225,7 +223,7 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
|
|||
|
||||
ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to read tmds config, err=%d\n", ret);
|
||||
DRM_ERROR("Failed to read TMDS config: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -236,7 +234,7 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
|
|||
|
||||
ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to set TMDS clock ratio, error %d\n", ret);
|
||||
DRM_ERROR("Failed to set TMDS clock ratio: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -417,8 +417,8 @@ static int drm_syncobj_fd_to_handle(struct drm_file *file_private,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
|
||||
int fd, int handle)
|
||||
static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
|
||||
int fd, int handle)
|
||||
{
|
||||
struct dma_fence *fence = sync_file_get_fence(fd);
|
||||
struct drm_syncobj *syncobj;
|
||||
|
@ -438,8 +438,8 @@ int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int drm_syncobj_export_sync_file(struct drm_file *file_private,
|
||||
int handle, int *p_fd)
|
||||
static int drm_syncobj_export_sync_file(struct drm_file *file_private,
|
||||
int handle, int *p_fd)
|
||||
{
|
||||
int ret;
|
||||
struct dma_fence *fence;
|
||||
|
|
|
@ -61,5 +61,5 @@ TRACE_EVENT(drm_vblank_event_delivered,
|
|||
|
||||
/* This part must be outside protection */
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm
|
||||
#include <trace/define_trace.h>
|
||||
|
|
|
@ -420,11 +420,7 @@ static int exynos_mic_probe(struct platform_device *pdev)
|
|||
mic->bridge.funcs = &mic_bridge_funcs;
|
||||
mic->bridge.of_node = dev->of_node;
|
||||
|
||||
ret = drm_bridge_add(&mic->bridge);
|
||||
if (ret) {
|
||||
DRM_ERROR("mic: Failed to add MIC to the global bridge list\n");
|
||||
return ret;
|
||||
}
|
||||
drm_bridge_add(&mic->bridge);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
|
|
|
@ -1901,10 +1901,8 @@ cdv_intel_dp_destroy(struct drm_connector *connector)
|
|||
|
||||
if (is_edp(gma_encoder)) {
|
||||
/* cdv_intel_panel_destroy_backlight(connector->dev); */
|
||||
if (intel_dp->panel_fixed_mode) {
|
||||
kfree(intel_dp->panel_fixed_mode);
|
||||
intel_dp->panel_fixed_mode = NULL;
|
||||
}
|
||||
kfree(intel_dp->panel_fixed_mode);
|
||||
intel_dp->panel_fixed_mode = NULL;
|
||||
}
|
||||
i2c_del_adapter(&intel_dp->adapter);
|
||||
drm_connector_unregister(connector);
|
||||
|
|
|
@ -99,7 +99,7 @@ void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe)
|
|||
/* Wait for for the pipe enable to take effect. */
|
||||
for (count = 0; count < COUNT_MAX; count++) {
|
||||
temp = REG_READ(map->conf);
|
||||
if ((temp & PIPEACONF_PIPE_STATE) == 1)
|
||||
if (temp & PIPEACONF_PIPE_STATE)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -485,7 +485,7 @@ static int ch7006_encoder_init(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_device_id ch7006_ids[] = {
|
||||
static const struct i2c_device_id ch7006_ids[] = {
|
||||
{ "ch7006", 0 },
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -415,7 +415,7 @@ sil164_encoder_init(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_device_id sil164_ids[] = {
|
||||
static const struct i2c_device_id sil164_ids[] = {
|
||||
{ "sil164", 0 },
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -1746,7 +1746,7 @@ static const struct of_device_id tda998x_dt_ids[] = {
|
|||
MODULE_DEVICE_TABLE(of, tda998x_dt_ids);
|
||||
#endif
|
||||
|
||||
static struct i2c_device_id tda998x_ids[] = {
|
||||
static const struct i2c_device_id tda998x_ids[] = {
|
||||
{ "tda998x", 0 },
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -707,8 +707,7 @@ struct drm_i915_display_funcs {
|
|||
struct drm_atomic_state *old_state);
|
||||
void (*crtc_disable)(struct intel_crtc_state *old_crtc_state,
|
||||
struct drm_atomic_state *old_state);
|
||||
void (*update_crtcs)(struct drm_atomic_state *state,
|
||||
unsigned int *crtc_vblank_mask);
|
||||
void (*update_crtcs)(struct drm_atomic_state *state);
|
||||
void (*audio_codec_enable)(struct drm_connector *connector,
|
||||
struct intel_encoder *encoder,
|
||||
const struct drm_display_mode *adjusted_mode);
|
||||
|
|
|
@ -12129,73 +12129,10 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
|
|||
return dev->driver->get_vblank_counter(dev, crtc->pipe);
|
||||
}
|
||||
|
||||
static void intel_atomic_wait_for_vblanks(struct drm_device *dev,
|
||||
struct drm_i915_private *dev_priv,
|
||||
unsigned crtc_mask)
|
||||
{
|
||||
unsigned last_vblank_count[I915_MAX_PIPES];
|
||||
enum pipe pipe;
|
||||
int ret;
|
||||
|
||||
if (!crtc_mask)
|
||||
return;
|
||||
|
||||
for_each_pipe(dev_priv, pipe) {
|
||||
struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
|
||||
pipe);
|
||||
|
||||
if (!((1 << pipe) & crtc_mask))
|
||||
continue;
|
||||
|
||||
ret = drm_crtc_vblank_get(&crtc->base);
|
||||
if (WARN_ON(ret != 0)) {
|
||||
crtc_mask &= ~(1 << pipe);
|
||||
continue;
|
||||
}
|
||||
|
||||
last_vblank_count[pipe] = drm_crtc_vblank_count(&crtc->base);
|
||||
}
|
||||
|
||||
for_each_pipe(dev_priv, pipe) {
|
||||
struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
|
||||
pipe);
|
||||
long lret;
|
||||
|
||||
if (!((1 << pipe) & crtc_mask))
|
||||
continue;
|
||||
|
||||
lret = wait_event_timeout(dev->vblank[pipe].queue,
|
||||
last_vblank_count[pipe] !=
|
||||
drm_crtc_vblank_count(&crtc->base),
|
||||
msecs_to_jiffies(50));
|
||||
|
||||
WARN(!lret, "pipe %c vblank wait timed out\n", pipe_name(pipe));
|
||||
|
||||
drm_crtc_vblank_put(&crtc->base);
|
||||
}
|
||||
}
|
||||
|
||||
static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
/* fb updated, need to unpin old fb */
|
||||
if (crtc_state->fb_changed)
|
||||
return true;
|
||||
|
||||
/* wm changes, need vblank before final wm's */
|
||||
if (crtc_state->update_wm_post)
|
||||
return true;
|
||||
|
||||
if (crtc_state->wm.need_postvbl_update)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void intel_update_crtc(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state,
|
||||
struct drm_crtc_state *old_crtc_state,
|
||||
struct drm_crtc_state *new_crtc_state,
|
||||
unsigned int *crtc_vblank_mask)
|
||||
struct drm_crtc_state *new_crtc_state)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
|
@ -12218,13 +12155,9 @@ static void intel_update_crtc(struct drm_crtc *crtc,
|
|||
}
|
||||
|
||||
drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
|
||||
|
||||
if (needs_vblank_wait(pipe_config))
|
||||
*crtc_vblank_mask |= drm_crtc_mask(crtc);
|
||||
}
|
||||
|
||||
static void intel_update_crtcs(struct drm_atomic_state *state,
|
||||
unsigned int *crtc_vblank_mask)
|
||||
static void intel_update_crtcs(struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
|
||||
|
@ -12235,12 +12168,11 @@ static void intel_update_crtcs(struct drm_atomic_state *state,
|
|||
continue;
|
||||
|
||||
intel_update_crtc(crtc, state, old_crtc_state,
|
||||
new_crtc_state, crtc_vblank_mask);
|
||||
new_crtc_state);
|
||||
}
|
||||
}
|
||||
|
||||
static void skl_update_crtcs(struct drm_atomic_state *state,
|
||||
unsigned int *crtc_vblank_mask)
|
||||
static void skl_update_crtcs(struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(state->dev);
|
||||
struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
|
||||
|
@ -12299,7 +12231,7 @@ static void skl_update_crtcs(struct drm_atomic_state *state,
|
|||
vbl_wait = true;
|
||||
|
||||
intel_update_crtc(crtc, state, old_crtc_state,
|
||||
new_crtc_state, crtc_vblank_mask);
|
||||
new_crtc_state);
|
||||
|
||||
if (vbl_wait)
|
||||
intel_wait_for_vblank(dev_priv, pipe);
|
||||
|
@ -12361,7 +12293,6 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
|
|||
struct intel_crtc_state *intel_cstate;
|
||||
bool hw_check = intel_state->modeset;
|
||||
u64 put_domains[I915_MAX_PIPES] = {};
|
||||
unsigned crtc_vblank_mask = 0;
|
||||
int i;
|
||||
|
||||
intel_atomic_commit_fence_wait(intel_state);
|
||||
|
@ -12451,7 +12382,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
|
|||
}
|
||||
|
||||
/* Now enable the clocks, plane, pipe, and connectors that we set up. */
|
||||
dev_priv->display.update_crtcs(state, &crtc_vblank_mask);
|
||||
dev_priv->display.update_crtcs(state);
|
||||
|
||||
/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
|
||||
* already, but still need the state for the delayed optimization. To
|
||||
|
@ -12462,8 +12393,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
|
|||
* - switch over to the vblank wait helper in the core after that since
|
||||
* we don't need out special handling any more.
|
||||
*/
|
||||
if (!state->legacy_cursor_update)
|
||||
intel_atomic_wait_for_vblanks(dev, dev_priv, crtc_vblank_mask);
|
||||
drm_atomic_helper_wait_for_flip_done(dev, state);
|
||||
|
||||
/*
|
||||
* Now that the vblank has passed, we can go ahead and program the
|
||||
|
@ -13061,6 +12991,14 @@ intel_legacy_cursor_update(struct drm_plane *plane,
|
|||
goto slow;
|
||||
|
||||
old_plane_state = plane->state;
|
||||
/*
|
||||
* Don't do an async update if there is an outstanding commit modifying
|
||||
* the plane. This prevents our async update's changes from getting
|
||||
* overridden by a previous synchronous update's state.
|
||||
*/
|
||||
if (old_plane_state->commit &&
|
||||
!try_wait_for_completion(&old_plane_state->commit->hw_done))
|
||||
goto slow;
|
||||
|
||||
/*
|
||||
* If any parameters change that may affect watermarks,
|
||||
|
@ -13120,17 +13058,12 @@ intel_legacy_cursor_update(struct drm_plane *plane,
|
|||
}
|
||||
|
||||
old_fb = old_plane_state->fb;
|
||||
old_vma = to_intel_plane_state(old_plane_state)->vma;
|
||||
|
||||
i915_gem_track_fb(intel_fb_obj(old_fb), intel_fb_obj(fb),
|
||||
intel_plane->frontbuffer_bit);
|
||||
|
||||
/* Swap plane state */
|
||||
new_plane_state->fence = old_plane_state->fence;
|
||||
*to_intel_plane_state(old_plane_state) = *to_intel_plane_state(new_plane_state);
|
||||
new_plane_state->fence = NULL;
|
||||
new_plane_state->fb = old_fb;
|
||||
to_intel_plane_state(new_plane_state)->vma = NULL;
|
||||
plane->state = new_plane_state;
|
||||
|
||||
if (plane->state->visible) {
|
||||
trace_intel_update_plane(plane, to_intel_crtc(crtc));
|
||||
|
@ -13142,13 +13075,17 @@ intel_legacy_cursor_update(struct drm_plane *plane,
|
|||
intel_plane->disable_plane(intel_plane, to_intel_crtc(crtc));
|
||||
}
|
||||
|
||||
old_vma = fetch_and_zero(&to_intel_plane_state(old_plane_state)->vma);
|
||||
if (old_vma)
|
||||
intel_unpin_fb_vma(old_vma);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&dev_priv->drm.struct_mutex);
|
||||
out_free:
|
||||
intel_plane_destroy_state(plane, new_plane_state);
|
||||
if (ret)
|
||||
intel_plane_destroy_state(plane, new_plane_state);
|
||||
else
|
||||
intel_plane_destroy_state(plane, old_plane_state);
|
||||
return ret;
|
||||
|
||||
slow:
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
|
@ -105,7 +106,7 @@ static int imx_drm_atomic_check(struct drm_device *dev,
|
|||
}
|
||||
|
||||
static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.fb_create = drm_gem_fb_create,
|
||||
.output_poll_changed = imx_drm_output_poll_changed,
|
||||
.atomic_check = imx_drm_atomic_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
|
||||
#include "video/imx-ipu-v3.h"
|
||||
|
@ -690,7 +691,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
|||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = {
|
||||
.prepare_fb = drm_fb_cma_prepare_fb,
|
||||
.prepare_fb = drm_gem_fb_prepare_fb,
|
||||
.atomic_check = ipu_plane_atomic_check,
|
||||
.atomic_disable = ipu_plane_atomic_disable,
|
||||
.atomic_update = ipu_plane_atomic_update,
|
||||
|
|
|
@ -1696,11 +1696,7 @@ static int mtk_drm_hdmi_probe(struct platform_device *pdev)
|
|||
|
||||
hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs;
|
||||
hdmi->bridge.of_node = pdev->dev.of_node;
|
||||
ret = drm_bridge_add(&hdmi->bridge);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add bridge, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
drm_bridge_add(&hdmi->bridge);
|
||||
|
||||
ret = mtk_hdmi_clk_enable_audio(hdmi);
|
||||
if (ret) {
|
||||
|
|
|
@ -63,6 +63,15 @@ config DRM_PANEL_LG_LG4573
|
|||
Say Y here if you want to enable support for LG4573 RGB panel.
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config DRM_PANEL_ORISETECH_OTM8009A
|
||||
tristate "Orise Technology otm8009a 480x800 dsi 2dl panel"
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Say Y here if you want to enable support for Orise Technology
|
||||
otm8009a 480x800 dsi 2dl panel.
|
||||
|
||||
config DRM_PANEL_PANASONIC_VVX10F034N00
|
||||
tristate "Panasonic VVX10F034N00 1920x1200 video mode panel"
|
||||
depends on OF
|
||||
|
@ -80,12 +89,28 @@ config DRM_PANEL_SAMSUNG_S6E3HA2
|
|||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select VIDEOMODE_HELPERS
|
||||
|
||||
config DRM_PANEL_SAMSUNG_S6E63J0X03
|
||||
tristate "Samsung S6E63J0X03 DSI command mode panel"
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select VIDEOMODE_HELPERS
|
||||
|
||||
config DRM_PANEL_SAMSUNG_S6E8AA0
|
||||
tristate "Samsung S6E8AA0 DSI video mode panel"
|
||||
depends on OF
|
||||
select DRM_MIPI_DSI
|
||||
select VIDEOMODE_HELPERS
|
||||
|
||||
config DRM_PANEL_SEIKO_43WVF1G
|
||||
tristate "Seiko 43WVF1G panel"
|
||||
depends on OF
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select VIDEOMODE_HELPERS
|
||||
help
|
||||
Say Y here if you want to enable support for the Seiko
|
||||
43WVF1G controller for 800x480 LCD panels
|
||||
|
||||
config DRM_PANEL_SHARP_LQ101R1SX01
|
||||
tristate "Sharp LQ101R1SX01 panel"
|
||||
depends on OF
|
||||
|
|
|
@ -3,10 +3,13 @@ obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
|
|||
obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
|
||||
obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
|
||||
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
|
||||
obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
|
||||
obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
|
||||
obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
|
||||
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
|
||||
obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
|
||||
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
|
||||
|
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* Copyright (C) STMicroelectronics SA 2017
|
||||
*
|
||||
* Authors: Philippe Cornu <philippe.cornu@st.com>
|
||||
* Yannick Fertre <yannick.fertre@st.com>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#define DRV_NAME "orisetech_otm8009a"
|
||||
|
||||
#define OTM8009A_BACKLIGHT_DEFAULT 240
|
||||
#define OTM8009A_BACKLIGHT_MAX 255
|
||||
|
||||
/* Manufacturer Command Set */
|
||||
#define MCS_ADRSFT 0x0000 /* Address Shift Function */
|
||||
#define MCS_PANSET 0xB3A6 /* Panel Type Setting */
|
||||
#define MCS_SD_CTRL 0xC0A2 /* Source Driver Timing Setting */
|
||||
#define MCS_P_DRV_M 0xC0B4 /* Panel Driving Mode */
|
||||
#define MCS_OSC_ADJ 0xC181 /* Oscillator Adjustment for Idle/Normal mode */
|
||||
#define MCS_RGB_VID_SET 0xC1A1 /* RGB Video Mode Setting */
|
||||
#define MCS_SD_PCH_CTRL 0xC480 /* Source Driver Precharge Control */
|
||||
#define MCS_NO_DOC1 0xC48A /* Command not documented */
|
||||
#define MCS_PWR_CTRL1 0xC580 /* Power Control Setting 1 */
|
||||
#define MCS_PWR_CTRL2 0xC590 /* Power Control Setting 2 for Normal Mode */
|
||||
#define MCS_PWR_CTRL4 0xC5B0 /* Power Control Setting 4 for DC Voltage */
|
||||
#define MCS_PANCTRLSET1 0xCB80 /* Panel Control Setting 1 */
|
||||
#define MCS_PANCTRLSET2 0xCB90 /* Panel Control Setting 2 */
|
||||
#define MCS_PANCTRLSET3 0xCBA0 /* Panel Control Setting 3 */
|
||||
#define MCS_PANCTRLSET4 0xCBB0 /* Panel Control Setting 4 */
|
||||
#define MCS_PANCTRLSET5 0xCBC0 /* Panel Control Setting 5 */
|
||||
#define MCS_PANCTRLSET6 0xCBD0 /* Panel Control Setting 6 */
|
||||
#define MCS_PANCTRLSET7 0xCBE0 /* Panel Control Setting 7 */
|
||||
#define MCS_PANCTRLSET8 0xCBF0 /* Panel Control Setting 8 */
|
||||
#define MCS_PANU2D1 0xCC80 /* Panel U2D Setting 1 */
|
||||
#define MCS_PANU2D2 0xCC90 /* Panel U2D Setting 2 */
|
||||
#define MCS_PANU2D3 0xCCA0 /* Panel U2D Setting 3 */
|
||||
#define MCS_PAND2U1 0xCCB0 /* Panel D2U Setting 1 */
|
||||
#define MCS_PAND2U2 0xCCC0 /* Panel D2U Setting 2 */
|
||||
#define MCS_PAND2U3 0xCCD0 /* Panel D2U Setting 3 */
|
||||
#define MCS_GOAVST 0xCE80 /* GOA VST Setting */
|
||||
#define MCS_GOACLKA1 0xCEA0 /* GOA CLKA1 Setting */
|
||||
#define MCS_GOACLKA3 0xCEB0 /* GOA CLKA3 Setting */
|
||||
#define MCS_GOAECLK 0xCFC0 /* GOA ECLK Setting */
|
||||
#define MCS_NO_DOC2 0xCFD0 /* Command not documented */
|
||||
#define MCS_GVDDSET 0xD800 /* GVDD/NGVDD */
|
||||
#define MCS_VCOMDC 0xD900 /* VCOM Voltage Setting */
|
||||
#define MCS_GMCT2_2P 0xE100 /* Gamma Correction 2.2+ Setting */
|
||||
#define MCS_GMCT2_2N 0xE200 /* Gamma Correction 2.2- Setting */
|
||||
#define MCS_NO_DOC3 0xF5B6 /* Command not documented */
|
||||
#define MCS_CMD2_ENA1 0xFF00 /* Enable Access Command2 "CMD2" */
|
||||
#define MCS_CMD2_ENA2 0xFF80 /* Enable Access Orise Command2 */
|
||||
|
||||
struct otm8009a {
|
||||
struct device *dev;
|
||||
struct drm_panel panel;
|
||||
struct backlight_device *bl_dev;
|
||||
struct gpio_desc *reset_gpio;
|
||||
bool prepared;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static const struct drm_display_mode default_mode = {
|
||||
.clock = 32729,
|
||||
.hdisplay = 480,
|
||||
.hsync_start = 480 + 120,
|
||||
.hsync_end = 480 + 120 + 63,
|
||||
.htotal = 480 + 120 + 63 + 120,
|
||||
.vdisplay = 800,
|
||||
.vsync_start = 800 + 12,
|
||||
.vsync_end = 800 + 12 + 12,
|
||||
.vtotal = 800 + 12 + 12 + 12,
|
||||
.vrefresh = 50,
|
||||
.flags = 0,
|
||||
.width_mm = 52,
|
||||
.height_mm = 86,
|
||||
};
|
||||
|
||||
static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct otm8009a, panel);
|
||||
}
|
||||
|
||||
static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
|
||||
size_t len)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
|
||||
if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
|
||||
DRM_WARN("mipi dsi dcs write buffer failed\n");
|
||||
}
|
||||
|
||||
#define dcs_write_seq(ctx, seq...) \
|
||||
({ \
|
||||
static const u8 d[] = { seq }; \
|
||||
otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d)); \
|
||||
})
|
||||
|
||||
#define dcs_write_cmd_at(ctx, cmd, seq...) \
|
||||
({ \
|
||||
dcs_write_seq(ctx, MCS_ADRSFT, (cmd) & 0xFF); \
|
||||
dcs_write_seq(ctx, (cmd) >> 8, seq); \
|
||||
})
|
||||
|
||||
static int otm8009a_init_sequence(struct otm8009a *ctx)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
int ret;
|
||||
|
||||
/* Enter CMD2 */
|
||||
dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01);
|
||||
|
||||
/* Enter Orise Command2 */
|
||||
dcs_write_cmd_at(ctx, MCS_CMD2_ENA2, 0x80, 0x09);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL, 0x30);
|
||||
mdelay(10);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_NO_DOC1, 0x40);
|
||||
mdelay(10);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL4 + 1, 0xA9);
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 1, 0x34);
|
||||
dcs_write_cmd_at(ctx, MCS_P_DRV_M, 0x50);
|
||||
dcs_write_cmd_at(ctx, MCS_VCOMDC, 0x4E);
|
||||
dcs_write_cmd_at(ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 2, 0x01);
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 5, 0x34);
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 4, 0x33);
|
||||
dcs_write_cmd_at(ctx, MCS_GVDDSET, 0x79, 0x79);
|
||||
dcs_write_cmd_at(ctx, MCS_SD_CTRL + 1, 0x1B);
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 2, 0x83);
|
||||
dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL + 1, 0x83);
|
||||
dcs_write_cmd_at(ctx, MCS_RGB_VID_SET, 0x0E);
|
||||
dcs_write_cmd_at(ctx, MCS_PANSET, 0x00, 0x01);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00,
|
||||
0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00,
|
||||
0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00,
|
||||
0x01, 0x02, 0x00, 0x00);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_NO_DOC2, 0x00);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
|
||||
4, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25,
|
||||
0x00, 0x00, 0x00, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
|
||||
dcs_write_cmd_at(ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26,
|
||||
0x00, 0x00, 0x00, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
|
||||
dcs_write_cmd_at(ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 1, 0x66);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_NO_DOC3, 0x06);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
|
||||
0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
|
||||
0x01);
|
||||
dcs_write_cmd_at(ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
|
||||
0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
|
||||
0x01);
|
||||
|
||||
/* Exit CMD2 */
|
||||
dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF);
|
||||
|
||||
ret = mipi_dsi_dcs_nop(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait for sleep out exit */
|
||||
mdelay(120);
|
||||
|
||||
/* Default portrait 480x800 rgb24 */
|
||||
dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
|
||||
|
||||
ret = mipi_dsi_dcs_set_column_address(dsi, 0,
|
||||
default_mode.hdisplay - 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* See otm8009a driver documentation for pixel format descriptions */
|
||||
ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |
|
||||
MIPI_DCS_PIXEL_FMT_24BIT << 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable CABC feature */
|
||||
dcs_write_seq(ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_on(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_dcs_nop(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Send Command GRAM memory write (no parameters) */
|
||||
dcs_write_seq(ctx, MIPI_DCS_WRITE_MEMORY_START);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otm8009a_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct otm8009a *ctx = panel_to_otm8009a(panel);
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
int ret;
|
||||
|
||||
if (!ctx->enabled)
|
||||
return 0; /* This is not an issue so we return 0 here */
|
||||
|
||||
/* Power off the backlight. Note: end-user still controls brightness */
|
||||
ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
|
||||
ret = backlight_update_status(ctx->bl_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_off(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msleep(120);
|
||||
|
||||
ctx->enabled = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otm8009a_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct otm8009a *ctx = panel_to_otm8009a(panel);
|
||||
|
||||
if (!ctx->prepared)
|
||||
return 0;
|
||||
|
||||
if (ctx->reset_gpio) {
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
ctx->prepared = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otm8009a_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct otm8009a *ctx = panel_to_otm8009a(panel);
|
||||
int ret;
|
||||
|
||||
if (ctx->prepared)
|
||||
return 0;
|
||||
|
||||
if (ctx->reset_gpio) {
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||
msleep(20);
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
ret = otm8009a_init_sequence(ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctx->prepared = true;
|
||||
|
||||
/*
|
||||
* Power on the backlight. Note: end-user still controls brightness
|
||||
* Note: ctx->prepared must be true before updating the backlight.
|
||||
*/
|
||||
ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
|
||||
backlight_update_status(ctx->bl_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otm8009a_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct otm8009a *ctx = panel_to_otm8009a(panel);
|
||||
|
||||
ctx->enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otm8009a_get_modes(struct drm_panel *panel)
|
||||
{
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(panel->drm, &default_mode);
|
||||
if (!mode) {
|
||||
DRM_ERROR("failed to add mode %ux%ux@%u\n",
|
||||
default_mode.hdisplay, default_mode.vdisplay,
|
||||
default_mode.vrefresh);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
|
||||
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
drm_mode_probed_add(panel->connector, mode);
|
||||
|
||||
panel->connector->display_info.width_mm = mode->width_mm;
|
||||
panel->connector->display_info.height_mm = mode->height_mm;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs otm8009a_drm_funcs = {
|
||||
.disable = otm8009a_disable,
|
||||
.unprepare = otm8009a_unprepare,
|
||||
.prepare = otm8009a_prepare,
|
||||
.enable = otm8009a_enable,
|
||||
.get_modes = otm8009a_get_modes,
|
||||
};
|
||||
|
||||
/*
|
||||
* DSI-BASED BACKLIGHT
|
||||
*/
|
||||
|
||||
static int otm8009a_backlight_update_status(struct backlight_device *bd)
|
||||
{
|
||||
struct otm8009a *ctx = bl_get_data(bd);
|
||||
u8 data[2];
|
||||
|
||||
if (!ctx->prepared) {
|
||||
DRM_DEBUG("lcd not ready yet for setting its backlight!\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (bd->props.power <= FB_BLANK_NORMAL) {
|
||||
/* Power on the backlight with the requested brightness
|
||||
* Note We can not use mipi_dsi_dcs_set_display_brightness()
|
||||
* as otm8009a driver support only 8-bit brightness (1 param).
|
||||
*/
|
||||
data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
|
||||
data[1] = bd->props.brightness;
|
||||
otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
|
||||
|
||||
/* set Brightness Control & Backlight on */
|
||||
data[1] = 0x24;
|
||||
|
||||
} else {
|
||||
/* Power off the backlight: set Brightness Control & Bl off */
|
||||
data[1] = 0;
|
||||
}
|
||||
|
||||
/* Update Brightness Control & Backlight */
|
||||
data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
|
||||
otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct backlight_ops otm8009a_backlight_ops = {
|
||||
.update_status = otm8009a_backlight_update_status,
|
||||
};
|
||||
|
||||
static int otm8009a_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct device *dev = &dsi->dev;
|
||||
struct otm8009a *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->reset_gpio)) {
|
||||
dev_err(dev, "cannot get reset-gpio\n");
|
||||
return PTR_ERR(ctx->reset_gpio);
|
||||
}
|
||||
|
||||
mipi_dsi_set_drvdata(dsi, ctx);
|
||||
|
||||
ctx->dev = dev;
|
||||
|
||||
dsi->lanes = 2;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
|
||||
MIPI_DSI_MODE_LPM;
|
||||
|
||||
drm_panel_init(&ctx->panel);
|
||||
ctx->panel.dev = dev;
|
||||
ctx->panel.funcs = &otm8009a_drm_funcs;
|
||||
|
||||
ctx->bl_dev = backlight_device_register(DRV_NAME "_backlight", dev, ctx,
|
||||
&otm8009a_backlight_ops, NULL);
|
||||
if (IS_ERR(ctx->bl_dev)) {
|
||||
dev_err(dev, "failed to register backlight device\n");
|
||||
return PTR_ERR(ctx->bl_dev);
|
||||
}
|
||||
|
||||
ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX;
|
||||
ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT;
|
||||
ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
|
||||
ctx->bl_dev->props.type = BACKLIGHT_RAW;
|
||||
|
||||
drm_panel_add(&ctx->panel);
|
||||
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n");
|
||||
drm_panel_remove(&ctx->panel);
|
||||
backlight_device_unregister(ctx->bl_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DRM_INFO(DRV_NAME "_panel %ux%u@%u %ubpp dsi %udl - ready\n",
|
||||
default_mode.hdisplay, default_mode.vdisplay,
|
||||
default_mode.vrefresh,
|
||||
mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otm8009a_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
mipi_dsi_detach(dsi);
|
||||
drm_panel_remove(&ctx->panel);
|
||||
|
||||
backlight_device_unregister(ctx->bl_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id orisetech_otm8009a_of_match[] = {
|
||||
{ .compatible = "orisetech,otm8009a" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match);
|
||||
|
||||
static struct mipi_dsi_driver orisetech_otm8009a_driver = {
|
||||
.probe = otm8009a_probe,
|
||||
.remove = otm8009a_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME "_panel",
|
||||
.of_match_table = orisetech_otm8009a_of_match,
|
||||
},
|
||||
};
|
||||
module_mipi_dsi_driver(orisetech_otm8009a_driver);
|
||||
|
||||
MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
|
||||
MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
|
||||
MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,532 @@
|
|||
/*
|
||||
* MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver.
|
||||
*
|
||||
* Copyright (c) 2014-2017 Samsung Electronics Co., Ltd
|
||||
*
|
||||
* Inki Dae <inki.dae@samsung.com>
|
||||
* Hoegeun Kwon <hoegeun.kwon@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#define MCS_LEVEL2_KEY 0xf0
|
||||
#define MCS_MTP_KEY 0xf1
|
||||
#define MCS_MTP_SET3 0xd4
|
||||
|
||||
#define MAX_BRIGHTNESS 100
|
||||
#define DEFAULT_BRIGHTNESS 80
|
||||
|
||||
#define NUM_GAMMA_STEPS 9
|
||||
#define GAMMA_CMD_CNT 28
|
||||
|
||||
#define FIRST_COLUMN 20
|
||||
|
||||
struct s6e63j0x03 {
|
||||
struct device *dev;
|
||||
struct drm_panel panel;
|
||||
struct backlight_device *bl_dev;
|
||||
|
||||
struct regulator_bulk_data supplies[2];
|
||||
struct gpio_desc *reset_gpio;
|
||||
};
|
||||
|
||||
static const struct drm_display_mode default_mode = {
|
||||
.clock = 4649,
|
||||
.hdisplay = 320,
|
||||
.hsync_start = 320 + 1,
|
||||
.hsync_end = 320 + 1 + 1,
|
||||
.htotal = 320 + 1 + 1 + 1,
|
||||
.vdisplay = 320,
|
||||
.vsync_start = 320 + 150,
|
||||
.vsync_end = 320 + 150 + 1,
|
||||
.vtotal = 320 + 150 + 1 + 2,
|
||||
.vrefresh = 30,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = {
|
||||
{ /* Gamma 10 */
|
||||
MCS_MTP_SET3,
|
||||
0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26,
|
||||
0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36,
|
||||
0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf
|
||||
},
|
||||
{ /* gamma 30 */
|
||||
MCS_MTP_SET3,
|
||||
0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26,
|
||||
0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34,
|
||||
0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc
|
||||
},
|
||||
{ /* gamma 60 */
|
||||
MCS_MTP_SET3,
|
||||
0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a,
|
||||
0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33,
|
||||
0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5
|
||||
},
|
||||
{ /* gamma 90 */
|
||||
MCS_MTP_SET3,
|
||||
0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29,
|
||||
0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31,
|
||||
0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09
|
||||
},
|
||||
{ /* gamma 120 */
|
||||
MCS_MTP_SET3,
|
||||
0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28,
|
||||
0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31,
|
||||
0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b
|
||||
},
|
||||
{ /* gamma 150 */
|
||||
MCS_MTP_SET3,
|
||||
0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28,
|
||||
0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31,
|
||||
0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29
|
||||
},
|
||||
{ /* gamma 200 */
|
||||
MCS_MTP_SET3,
|
||||
0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29,
|
||||
0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30,
|
||||
0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b
|
||||
},
|
||||
{ /* gamma 240 */
|
||||
MCS_MTP_SET3,
|
||||
0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a,
|
||||
0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30,
|
||||
0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47
|
||||
},
|
||||
{ /* gamma 300 */
|
||||
MCS_MTP_SET3,
|
||||
0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28,
|
||||
0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f,
|
||||
0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57
|
||||
}
|
||||
};
|
||||
|
||||
static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct s6e63j0x03, panel);
|
||||
}
|
||||
|
||||
static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx,
|
||||
const void *seq, size_t len)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
|
||||
return mipi_dsi_dcs_write_buffer(dsi, seq, len);
|
||||
}
|
||||
|
||||
#define s6e63j0x03_dcs_write_seq_static(ctx, seq...) \
|
||||
({ \
|
||||
static const u8 d[] = { seq }; \
|
||||
s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d)); \
|
||||
})
|
||||
|
||||
static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx)
|
||||
{
|
||||
return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a);
|
||||
}
|
||||
|
||||
static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on)
|
||||
{
|
||||
if (on)
|
||||
return s6e63j0x03_dcs_write_seq_static(ctx,
|
||||
MCS_MTP_KEY, 0x5a, 0x5a);
|
||||
|
||||
return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5);
|
||||
}
|
||||
|
||||
static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
msleep(30);
|
||||
|
||||
gpiod_set_value(ctx->reset_gpio, 1);
|
||||
usleep_range(1000, 2000);
|
||||
gpiod_set_value(ctx->reset_gpio, 0);
|
||||
usleep_range(5000, 6000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
|
||||
{
|
||||
return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
}
|
||||
|
||||
static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness)
|
||||
{
|
||||
unsigned int index;
|
||||
|
||||
index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS);
|
||||
|
||||
if (index >= NUM_GAMMA_STEPS)
|
||||
index = NUM_GAMMA_STEPS - 1;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx,
|
||||
unsigned int brightness)
|
||||
{
|
||||
struct backlight_device *bl_dev = ctx->bl_dev;
|
||||
unsigned int index = s6e63j0x03_get_brightness_index(brightness);
|
||||
int ret;
|
||||
|
||||
ret = s6e63j0x03_apply_mtp_key(ctx, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = s6e63j0x03_apply_mtp_key(ctx, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
bl_dev->props.brightness = brightness;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev)
|
||||
{
|
||||
struct s6e63j0x03 *ctx = bl_get_data(bl_dev);
|
||||
unsigned int brightness = bl_dev->props.brightness;
|
||||
|
||||
return s6e63j0x03_update_gamma(ctx, brightness);
|
||||
}
|
||||
|
||||
static const struct backlight_ops s6e63j0x03_bl_ops = {
|
||||
.update_status = s6e63j0x03_set_brightness,
|
||||
};
|
||||
|
||||
static int s6e63j0x03_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
int ret;
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_off(dsi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx->bl_dev->props.power = FB_BLANK_NORMAL;
|
||||
|
||||
ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
msleep(120);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s6e63j0x03_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
|
||||
int ret;
|
||||
|
||||
ret = s6e63j0x03_power_off(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
int ret;
|
||||
|
||||
ret = s6e63j0x03_enable_lv2_command(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = s6e63j0x03_apply_mtp_key(ctx, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set porch adjustment */
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set frame freq */
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set caset, paset */
|
||||
ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN,
|
||||
default_mode.hdisplay - 1 + FIRST_COLUMN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set ltps timming 0, 1 */
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17,
|
||||
0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set param pos te_edge */
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set te rising edge */
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set param pos default */
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = s6e63j0x03_apply_mtp_key(ctx, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s6e63j0x03_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
|
||||
int ret;
|
||||
|
||||
ret = s6e63j0x03_power_on(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = s6e63j0x03_panel_init(ctx);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ctx->bl_dev->props.power = FB_BLANK_NORMAL;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
s6e63j0x03_power_off(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s6e63j0x03_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
int ret;
|
||||
|
||||
msleep(120);
|
||||
|
||||
ret = s6e63j0x03_apply_mtp_key(ctx, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set elvss_cond */
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set pos */
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx,
|
||||
MIPI_DCS_SET_ADDRESS_MODE, 0x40);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set default white brightness */
|
||||
ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set white ctrl */
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx,
|
||||
MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set acl off */
|
||||
ret = s6e63j0x03_dcs_write_seq_static(ctx,
|
||||
MIPI_DCS_WRITE_POWER_SAVE, 0x00);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = s6e63j0x03_apply_mtp_key(ctx, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_on(dsi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s6e63j0x03_get_modes(struct drm_panel *panel)
|
||||
{
|
||||
struct drm_connector *connector = panel->connector;
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(panel->drm, &default_mode);
|
||||
if (!mode) {
|
||||
DRM_ERROR("failed to add mode %ux%ux@%u\n",
|
||||
default_mode.hdisplay, default_mode.vdisplay,
|
||||
default_mode.vrefresh);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
|
||||
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
connector->display_info.width_mm = 29;
|
||||
connector->display_info.height_mm = 29;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs s6e63j0x03_funcs = {
|
||||
.disable = s6e63j0x03_disable,
|
||||
.unprepare = s6e63j0x03_unprepare,
|
||||
.prepare = s6e63j0x03_prepare,
|
||||
.enable = s6e63j0x03_enable,
|
||||
.get_modes = s6e63j0x03_get_modes,
|
||||
};
|
||||
|
||||
static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct device *dev = &dsi->dev;
|
||||
struct s6e63j0x03 *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
mipi_dsi_set_drvdata(dsi, ctx);
|
||||
|
||||
ctx->dev = dev;
|
||||
|
||||
dsi->lanes = 1;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_EOT_PACKET;
|
||||
|
||||
ctx->supplies[0].supply = "vdd3";
|
||||
ctx->supplies[1].supply = "vci";
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
|
||||
ctx->supplies);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to get regulators: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->reset_gpio)) {
|
||||
dev_err(dev, "cannot get reset-gpio: %ld\n",
|
||||
PTR_ERR(ctx->reset_gpio));
|
||||
return PTR_ERR(ctx->reset_gpio);
|
||||
}
|
||||
|
||||
drm_panel_init(&ctx->panel);
|
||||
ctx->panel.dev = dev;
|
||||
ctx->panel.funcs = &s6e63j0x03_funcs;
|
||||
|
||||
ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,
|
||||
&s6e63j0x03_bl_ops, NULL);
|
||||
if (IS_ERR(ctx->bl_dev)) {
|
||||
dev_err(dev, "failed to register backlight device\n");
|
||||
return PTR_ERR(ctx->bl_dev);
|
||||
}
|
||||
|
||||
ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS;
|
||||
ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS;
|
||||
ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
|
||||
|
||||
ret = drm_panel_add(&ctx->panel);
|
||||
if (ret < 0)
|
||||
goto unregister_backlight;
|
||||
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret < 0)
|
||||
goto remove_panel;
|
||||
|
||||
return ret;
|
||||
|
||||
remove_panel:
|
||||
drm_panel_remove(&ctx->panel);
|
||||
|
||||
unregister_backlight:
|
||||
backlight_device_unregister(ctx->bl_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s6e63j0x03_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
mipi_dsi_detach(dsi);
|
||||
drm_panel_remove(&ctx->panel);
|
||||
|
||||
backlight_device_unregister(ctx->bl_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id s6e63j0x03_of_match[] = {
|
||||
{ .compatible = "samsung,s6e63j0x03" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
|
||||
|
||||
static struct mipi_dsi_driver s6e63j0x03_driver = {
|
||||
.probe = s6e63j0x03_probe,
|
||||
.remove = s6e63j0x03_remove,
|
||||
.driver = {
|
||||
.name = "panel_samsung_s6e63j0x03",
|
||||
.of_match_table = s6e63j0x03_of_match,
|
||||
},
|
||||
};
|
||||
module_mipi_dsi_driver(s6e63j0x03_driver);
|
||||
|
||||
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
|
||||
MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
|
||||
MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
* Copyright (C) 2017 NXP Semiconductors.
|
||||
* Author: Marco Franchi <marco.franchi@nxp.com>
|
||||
*
|
||||
* Based on Panel Simple driver by Thierry Reding <treding@nvidia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include <video/display_timing.h>
|
||||
#include <video/videomode.h>
|
||||
|
||||
struct seiko_panel_desc {
|
||||
const struct drm_display_mode *modes;
|
||||
unsigned int num_modes;
|
||||
const struct display_timing *timings;
|
||||
unsigned int num_timings;
|
||||
|
||||
unsigned int bpc;
|
||||
|
||||
/**
|
||||
* @width: width (in millimeters) of the panel's active display area
|
||||
* @height: height (in millimeters) of the panel's active display area
|
||||
*/
|
||||
struct {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
} size;
|
||||
|
||||
u32 bus_format;
|
||||
u32 bus_flags;
|
||||
};
|
||||
|
||||
struct seiko_panel {
|
||||
struct drm_panel base;
|
||||
bool prepared;
|
||||
bool enabled;
|
||||
const struct seiko_panel_desc *desc;
|
||||
struct backlight_device *backlight;
|
||||
struct regulator *dvdd;
|
||||
struct regulator *avdd;
|
||||
};
|
||||
|
||||
static inline struct seiko_panel *to_seiko_panel(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct seiko_panel, base);
|
||||
}
|
||||
|
||||
static int seiko_panel_get_fixed_modes(struct seiko_panel *panel)
|
||||
{
|
||||
struct drm_connector *connector = panel->base.connector;
|
||||
struct drm_device *drm = panel->base.drm;
|
||||
struct drm_display_mode *mode;
|
||||
unsigned int i, num = 0;
|
||||
|
||||
if (!panel->desc)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < panel->desc->num_timings; i++) {
|
||||
const struct display_timing *dt = &panel->desc->timings[i];
|
||||
struct videomode vm;
|
||||
|
||||
videomode_from_timing(dt, &vm);
|
||||
mode = drm_mode_create(drm);
|
||||
if (!mode) {
|
||||
dev_err(drm->dev, "failed to add mode %ux%u\n",
|
||||
dt->hactive.typ, dt->vactive.typ);
|
||||
continue;
|
||||
}
|
||||
|
||||
drm_display_mode_from_videomode(&vm, mode);
|
||||
|
||||
mode->type |= DRM_MODE_TYPE_DRIVER;
|
||||
|
||||
if (panel->desc->num_timings == 1)
|
||||
mode->type |= DRM_MODE_TYPE_PREFERRED;
|
||||
|
||||
drm_mode_probed_add(connector, mode);
|
||||
num++;
|
||||
}
|
||||
|
||||
for (i = 0; i < panel->desc->num_modes; i++) {
|
||||
const struct drm_display_mode *m = &panel->desc->modes[i];
|
||||
|
||||
mode = drm_mode_duplicate(drm, m);
|
||||
if (!mode) {
|
||||
dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
|
||||
m->hdisplay, m->vdisplay, m->vrefresh);
|
||||
continue;
|
||||
}
|
||||
|
||||
mode->type |= DRM_MODE_TYPE_DRIVER;
|
||||
|
||||
if (panel->desc->num_modes == 1)
|
||||
mode->type |= DRM_MODE_TYPE_PREFERRED;
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
|
||||
drm_mode_probed_add(connector, mode);
|
||||
num++;
|
||||
}
|
||||
|
||||
connector->display_info.bpc = panel->desc->bpc;
|
||||
connector->display_info.width_mm = panel->desc->size.width;
|
||||
connector->display_info.height_mm = panel->desc->size.height;
|
||||
if (panel->desc->bus_format)
|
||||
drm_display_info_set_bus_formats(&connector->display_info,
|
||||
&panel->desc->bus_format, 1);
|
||||
connector->display_info.bus_flags = panel->desc->bus_flags;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static int seiko_panel_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct seiko_panel *p = to_seiko_panel(panel);
|
||||
|
||||
if (!p->enabled)
|
||||
return 0;
|
||||
|
||||
if (p->backlight) {
|
||||
p->backlight->props.power = FB_BLANK_POWERDOWN;
|
||||
p->backlight->props.state |= BL_CORE_FBBLANK;
|
||||
backlight_update_status(p->backlight);
|
||||
}
|
||||
|
||||
p->enabled = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seiko_panel_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct seiko_panel *p = to_seiko_panel(panel);
|
||||
|
||||
if (!p->prepared)
|
||||
return 0;
|
||||
|
||||
regulator_disable(p->avdd);
|
||||
|
||||
/* Add a 100ms delay as per the panel datasheet */
|
||||
msleep(100);
|
||||
|
||||
regulator_disable(p->dvdd);
|
||||
|
||||
p->prepared = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seiko_panel_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct seiko_panel *p = to_seiko_panel(panel);
|
||||
int err;
|
||||
|
||||
if (p->prepared)
|
||||
return 0;
|
||||
|
||||
err = regulator_enable(p->dvdd);
|
||||
if (err < 0) {
|
||||
dev_err(panel->dev, "failed to enable dvdd: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Add a 100ms delay as per the panel datasheet */
|
||||
msleep(100);
|
||||
|
||||
err = regulator_enable(p->avdd);
|
||||
if (err < 0) {
|
||||
dev_err(panel->dev, "failed to enable avdd: %d\n", err);
|
||||
goto disable_dvdd;
|
||||
}
|
||||
|
||||
p->prepared = true;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_dvdd:
|
||||
regulator_disable(p->dvdd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int seiko_panel_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct seiko_panel *p = to_seiko_panel(panel);
|
||||
|
||||
if (p->enabled)
|
||||
return 0;
|
||||
|
||||
if (p->backlight) {
|
||||
p->backlight->props.state &= ~BL_CORE_FBBLANK;
|
||||
p->backlight->props.power = FB_BLANK_UNBLANK;
|
||||
backlight_update_status(p->backlight);
|
||||
}
|
||||
|
||||
p->enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seiko_panel_get_modes(struct drm_panel *panel)
|
||||
{
|
||||
struct seiko_panel *p = to_seiko_panel(panel);
|
||||
|
||||
/* add hard-coded panel modes */
|
||||
return seiko_panel_get_fixed_modes(p);
|
||||
}
|
||||
|
||||
static int seiko_panel_get_timings(struct drm_panel *panel,
|
||||
unsigned int num_timings,
|
||||
struct display_timing *timings)
|
||||
{
|
||||
struct seiko_panel *p = to_seiko_panel(panel);
|
||||
unsigned int i;
|
||||
|
||||
if (p->desc->num_timings < num_timings)
|
||||
num_timings = p->desc->num_timings;
|
||||
|
||||
if (timings)
|
||||
for (i = 0; i < num_timings; i++)
|
||||
timings[i] = p->desc->timings[i];
|
||||
|
||||
return p->desc->num_timings;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs seiko_panel_funcs = {
|
||||
.disable = seiko_panel_disable,
|
||||
.unprepare = seiko_panel_unprepare,
|
||||
.prepare = seiko_panel_prepare,
|
||||
.enable = seiko_panel_enable,
|
||||
.get_modes = seiko_panel_get_modes,
|
||||
.get_timings = seiko_panel_get_timings,
|
||||
};
|
||||
|
||||
static int seiko_panel_probe(struct device *dev,
|
||||
const struct seiko_panel_desc *desc)
|
||||
{
|
||||
struct device_node *backlight;
|
||||
struct seiko_panel *panel;
|
||||
int err;
|
||||
|
||||
panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
|
||||
if (!panel)
|
||||
return -ENOMEM;
|
||||
|
||||
panel->enabled = false;
|
||||
panel->prepared = false;
|
||||
panel->desc = desc;
|
||||
|
||||
panel->dvdd = devm_regulator_get(dev, "dvdd");
|
||||
if (IS_ERR(panel->dvdd))
|
||||
return PTR_ERR(panel->dvdd);
|
||||
|
||||
panel->avdd = devm_regulator_get(dev, "avdd");
|
||||
if (IS_ERR(panel->avdd))
|
||||
return PTR_ERR(panel->avdd);
|
||||
|
||||
backlight = of_parse_phandle(dev->of_node, "backlight", 0);
|
||||
if (backlight) {
|
||||
panel->backlight = of_find_backlight_by_node(backlight);
|
||||
of_node_put(backlight);
|
||||
|
||||
if (!panel->backlight)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
drm_panel_init(&panel->base);
|
||||
panel->base.dev = dev;
|
||||
panel->base.funcs = &seiko_panel_funcs;
|
||||
|
||||
err = drm_panel_add(&panel->base);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
dev_set_drvdata(dev, panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seiko_panel_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct seiko_panel *panel = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
drm_panel_detach(&panel->base);
|
||||
drm_panel_remove(&panel->base);
|
||||
|
||||
seiko_panel_disable(&panel->base);
|
||||
|
||||
if (panel->backlight)
|
||||
put_device(&panel->backlight->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void seiko_panel_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct seiko_panel *panel = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
seiko_panel_disable(&panel->base);
|
||||
}
|
||||
|
||||
static const struct display_timing seiko_43wvf1g_timing = {
|
||||
.pixelclock = { 33500000, 33500000, 33500000 },
|
||||
.hactive = { 800, 800, 800 },
|
||||
.hfront_porch = { 164, 164, 164 },
|
||||
.hback_porch = { 89, 89, 89 },
|
||||
.hsync_len = { 10, 10, 10 },
|
||||
.vactive = { 480, 480, 480 },
|
||||
.vfront_porch = { 10, 10, 10 },
|
||||
.vback_porch = { 23, 23, 23 },
|
||||
.vsync_len = { 10, 10, 10 },
|
||||
.flags = DISPLAY_FLAGS_DE_LOW,
|
||||
};
|
||||
|
||||
static const struct seiko_panel_desc seiko_43wvf1g = {
|
||||
.timings = &seiko_43wvf1g_timing,
|
||||
.num_timings = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 93,
|
||||
.height = 57,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE,
|
||||
};
|
||||
|
||||
static const struct of_device_id platform_of_match[] = {
|
||||
{
|
||||
.compatible = "sii,43wvf1g",
|
||||
.data = &seiko_43wvf1g,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, platform_of_match);
|
||||
|
||||
static int seiko_panel_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *id;
|
||||
|
||||
id = of_match_node(platform_of_match, pdev->dev.of_node);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
return seiko_panel_probe(&pdev->dev, id->data);
|
||||
}
|
||||
|
||||
static struct platform_driver seiko_panel_platform_driver = {
|
||||
.driver = {
|
||||
.name = "seiko_panel",
|
||||
.of_match_table = platform_of_match,
|
||||
},
|
||||
.probe = seiko_panel_platform_probe,
|
||||
.remove = seiko_panel_remove,
|
||||
.shutdown = seiko_panel_shutdown,
|
||||
};
|
||||
module_platform_driver(seiko_panel_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Marco Franchi <marco.franchi@nxp.com");
|
||||
MODULE_DESCRIPTION("Seiko 43WVF1G panel driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -187,8 +187,7 @@ static int panel_simple_unprepare(struct drm_panel *panel)
|
|||
if (!p->prepared)
|
||||
return 0;
|
||||
|
||||
if (p->enable_gpio)
|
||||
gpiod_set_value_cansleep(p->enable_gpio, 0);
|
||||
gpiod_set_value_cansleep(p->enable_gpio, 0);
|
||||
|
||||
regulator_disable(p->supply);
|
||||
|
||||
|
@ -214,8 +213,7 @@ static int panel_simple_prepare(struct drm_panel *panel)
|
|||
return err;
|
||||
}
|
||||
|
||||
if (p->enable_gpio)
|
||||
gpiod_set_value_cansleep(p->enable_gpio, 1);
|
||||
gpiod_set_value_cansleep(p->enable_gpio, 1);
|
||||
|
||||
if (p->desc->delay.prepare)
|
||||
msleep(p->desc->delay.prepare);
|
||||
|
@ -315,7 +313,8 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
|
|||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(panel->enable_gpio)) {
|
||||
err = PTR_ERR(panel->enable_gpio);
|
||||
dev_err(dev, "failed to request GPIO: %d\n", err);
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to request GPIO: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -369,6 +368,7 @@ static int panel_simple_remove(struct device *dev)
|
|||
drm_panel_remove(&panel->base);
|
||||
|
||||
panel_simple_disable(&panel->base);
|
||||
panel_simple_unprepare(&panel->base);
|
||||
|
||||
if (panel->ddc)
|
||||
put_device(&panel->ddc->dev);
|
||||
|
@ -384,6 +384,7 @@ static void panel_simple_shutdown(struct device *dev)
|
|||
struct panel_simple *panel = dev_get_drvdata(dev);
|
||||
|
||||
panel_simple_disable(&panel->base);
|
||||
panel_simple_unprepare(&panel->base);
|
||||
}
|
||||
|
||||
static const struct drm_display_mode ampire_am_480272h3tmqw_t01h_mode = {
|
||||
|
@ -1522,8 +1523,8 @@ static const struct panel_desc olimex_lcd_olinuxino_43ts = {
|
|||
.modes = &olimex_lcd_olinuxino_43ts_mode,
|
||||
.num_modes = 1,
|
||||
.size = {
|
||||
.width = 105,
|
||||
.height = 67,
|
||||
.width = 95,
|
||||
.height = 54,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
};
|
||||
|
|
|
@ -6,7 +6,8 @@ config DRM_PL111
|
|||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select DRM_PANEL
|
||||
select DRM_BRIDGE
|
||||
select DRM_PANEL_BRIDGE
|
||||
select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
|
||||
help
|
||||
Choose this option for DRM support for the PL111 CLCD controller.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pl111_drm-y += pl111_connector.o \
|
||||
pl111_display.o \
|
||||
pl111_drm-y += pl111_display.o \
|
||||
pl111_versatile.o \
|
||||
pl111_drv.o
|
||||
|
||||
pl111_drm-$(CONFIG_DEBUG_FS) += pl111_debugfs.o
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
|
||||
*
|
||||
* Parts of this file were based on sources as follows:
|
||||
*
|
||||
* Copyright (c) 2006-2008 Intel Corporation
|
||||
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
|
||||
* Copyright (C) 2011 Texas Instruments
|
||||
*
|
||||
* This program is free software and is provided to you under the terms of the
|
||||
* GNU General Public License version 2 as published by the Free Software
|
||||
* Foundation, and any use by you of this program is subject to the terms of
|
||||
* such GNU licence.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* pl111_drm_connector.c
|
||||
* Implementation of the connector functions for PL111 DRM
|
||||
*/
|
||||
#include <linux/amba/clcd-regs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/dma-buf.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include "pl111_drm.h"
|
||||
|
||||
static void pl111_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct pl111_drm_connector *pl111_connector =
|
||||
to_pl111_connector(connector);
|
||||
|
||||
if (pl111_connector->panel)
|
||||
drm_panel_detach(pl111_connector->panel);
|
||||
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static enum drm_connector_status pl111_connector_detect(struct drm_connector
|
||||
*connector, bool force)
|
||||
{
|
||||
struct pl111_drm_connector *pl111_connector =
|
||||
to_pl111_connector(connector);
|
||||
|
||||
return (pl111_connector->panel ?
|
||||
connector_status_connected :
|
||||
connector_status_disconnected);
|
||||
}
|
||||
|
||||
static int pl111_connector_helper_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct pl111_drm_connector *pl111_connector =
|
||||
to_pl111_connector(connector);
|
||||
|
||||
if (!pl111_connector->panel)
|
||||
return 0;
|
||||
|
||||
return drm_panel_get_modes(pl111_connector->panel);
|
||||
}
|
||||
|
||||
const struct drm_connector_funcs connector_funcs = {
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = pl111_connector_destroy,
|
||||
.detect = pl111_connector_detect,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
const struct drm_connector_helper_funcs connector_helper_funcs = {
|
||||
.get_modes = pl111_connector_helper_get_modes,
|
||||
};
|
||||
|
||||
/* Walks the OF graph to find the panel node and then asks DRM to look
|
||||
* up the panel.
|
||||
*/
|
||||
static struct drm_panel *pl111_get_panel(struct device *dev)
|
||||
{
|
||||
struct device_node *endpoint, *panel_node;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct drm_panel *panel;
|
||||
|
||||
endpoint = of_graph_get_next_endpoint(np, NULL);
|
||||
if (!endpoint) {
|
||||
dev_err(dev, "no endpoint to fetch panel\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* don't proceed if we have an endpoint but no panel_node tied to it */
|
||||
panel_node = of_graph_get_remote_port_parent(endpoint);
|
||||
of_node_put(endpoint);
|
||||
if (!panel_node) {
|
||||
dev_err(dev, "no valid panel node\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
panel = of_drm_find_panel(panel_node);
|
||||
of_node_put(panel_node);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
int pl111_connector_init(struct drm_device *dev)
|
||||
{
|
||||
struct pl111_drm_dev_private *priv = dev->dev_private;
|
||||
struct pl111_drm_connector *pl111_connector = &priv->connector;
|
||||
struct drm_connector *connector = &pl111_connector->connector;
|
||||
|
||||
drm_connector_init(dev, connector, &connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DPI);
|
||||
drm_connector_helper_add(connector, &connector_helper_funcs);
|
||||
|
||||
pl111_connector->panel = pl111_get_panel(dev->dev);
|
||||
if (pl111_connector->panel)
|
||||
drm_panel_attach(pl111_connector->panel, connector);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -22,8 +22,14 @@ static const struct {
|
|||
REGDEF(CLCD_TIM2),
|
||||
REGDEF(CLCD_TIM3),
|
||||
REGDEF(CLCD_UBAS),
|
||||
REGDEF(CLCD_LBAS),
|
||||
REGDEF(CLCD_PL111_CNTL),
|
||||
REGDEF(CLCD_PL111_IENB),
|
||||
REGDEF(CLCD_PL111_RIS),
|
||||
REGDEF(CLCD_PL111_MIS),
|
||||
REGDEF(CLCD_PL111_ICR),
|
||||
REGDEF(CLCD_PL111_UCUR),
|
||||
REGDEF(CLCD_PL111_LCUR),
|
||||
};
|
||||
|
||||
int pl111_debugfs_regs(struct seq_file *m, void *unused)
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/of_graph.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
|
@ -94,7 +93,7 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
const struct drm_display_mode *mode = &cstate->mode;
|
||||
struct drm_framebuffer *fb = plane->state->fb;
|
||||
struct drm_connector *connector = &priv->connector.connector;
|
||||
struct drm_connector *connector = priv->connector;
|
||||
u32 cntl;
|
||||
u32 ppl, hsw, hfp, hbp;
|
||||
u32 lpp, vsw, vfp, vbp;
|
||||
|
@ -156,10 +155,8 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
|
||||
writel(0, priv->regs + CLCD_TIM3);
|
||||
|
||||
drm_panel_prepare(priv->connector.panel);
|
||||
|
||||
/* Enable and Power Up */
|
||||
cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1);
|
||||
/* Hard-code TFT panel */
|
||||
cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDVCOMP(1);
|
||||
|
||||
/* Note that the the hardware's format reader takes 'r' from
|
||||
* the low bit, while DRM formats list channels from high bit
|
||||
|
@ -202,9 +199,21 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
break;
|
||||
}
|
||||
|
||||
writel(cntl, priv->regs + CLCD_PL111_CNTL);
|
||||
/* Power sequence: first enable and chill */
|
||||
writel(cntl, priv->regs + priv->ctrl);
|
||||
|
||||
drm_panel_enable(priv->connector.panel);
|
||||
/*
|
||||
* We expect this delay to stabilize the contrast
|
||||
* voltage Vee as stipulated by the manual
|
||||
*/
|
||||
msleep(20);
|
||||
|
||||
if (priv->variant_display_enable)
|
||||
priv->variant_display_enable(drm, fb->format->format);
|
||||
|
||||
/* Power Up */
|
||||
cntl |= CNTL_LCDPWR;
|
||||
writel(cntl, priv->regs + priv->ctrl);
|
||||
|
||||
drm_crtc_vblank_on(crtc);
|
||||
}
|
||||
|
@ -214,15 +223,28 @@ void pl111_display_disable(struct drm_simple_display_pipe *pipe)
|
|||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
u32 cntl;
|
||||
|
||||
drm_crtc_vblank_off(crtc);
|
||||
|
||||
drm_panel_disable(priv->connector.panel);
|
||||
/* Power Down */
|
||||
cntl = readl(priv->regs + priv->ctrl);
|
||||
if (cntl & CNTL_LCDPWR) {
|
||||
cntl &= ~CNTL_LCDPWR;
|
||||
writel(cntl, priv->regs + priv->ctrl);
|
||||
}
|
||||
|
||||
/* Disable and Power Down */
|
||||
writel(0, priv->regs + CLCD_PL111_CNTL);
|
||||
/*
|
||||
* We expect this delay to stabilize the contrast voltage Vee as
|
||||
* stipulated by the manual
|
||||
*/
|
||||
msleep(20);
|
||||
|
||||
drm_panel_unprepare(priv->connector.panel);
|
||||
if (priv->variant_display_disable)
|
||||
priv->variant_display_disable(drm);
|
||||
|
||||
/* Disable */
|
||||
writel(0, priv->regs + priv->ctrl);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
}
|
||||
|
@ -260,7 +282,7 @@ int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc)
|
|||
{
|
||||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
|
||||
writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB);
|
||||
writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + priv->ienb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -269,7 +291,7 @@ void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc)
|
|||
{
|
||||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
|
||||
writel(0, priv->regs + CLCD_PL111_IENB);
|
||||
writel(0, priv->regs + priv->ienb);
|
||||
}
|
||||
|
||||
static int pl111_display_prepare_fb(struct drm_simple_display_pipe *pipe,
|
||||
|
@ -413,22 +435,6 @@ int pl111_display_init(struct drm_device *drm)
|
|||
struct device_node *endpoint;
|
||||
u32 tft_r0b0g0[3];
|
||||
int ret;
|
||||
static const u32 formats[] = {
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_BGR565,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_ABGR1555,
|
||||
DRM_FORMAT_XBGR1555,
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_XRGB1555,
|
||||
DRM_FORMAT_ABGR4444,
|
||||
DRM_FORMAT_XBGR4444,
|
||||
DRM_FORMAT_ARGB4444,
|
||||
DRM_FORMAT_XRGB4444,
|
||||
};
|
||||
|
||||
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
|
||||
if (!endpoint)
|
||||
|
@ -444,21 +450,16 @@ int pl111_display_init(struct drm_device *drm)
|
|||
}
|
||||
of_node_put(endpoint);
|
||||
|
||||
if (tft_r0b0g0[0] != 0 ||
|
||||
tft_r0b0g0[1] != 8 ||
|
||||
tft_r0b0g0[2] != 16) {
|
||||
dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = pl111_init_clock_divider(drm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_simple_display_pipe_init(drm, &priv->pipe,
|
||||
&pl111_display_funcs,
|
||||
formats, ARRAY_SIZE(formats),
|
||||
NULL, &priv->connector.connector);
|
||||
priv->variant->formats,
|
||||
priv->variant->nformats,
|
||||
NULL,
|
||||
priv->connector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -21,25 +21,43 @@
|
|||
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define CLCD_IRQ_NEXTBASE_UPDATE BIT(2)
|
||||
|
||||
struct drm_minor;
|
||||
|
||||
struct pl111_drm_connector {
|
||||
struct drm_connector connector;
|
||||
struct drm_panel *panel;
|
||||
/**
|
||||
* struct pl111_variant_data - encodes IP differences
|
||||
* @name: the name of this variant
|
||||
* @is_pl110: this is the early PL110 variant
|
||||
* @formats: array of supported pixel formats on this variant
|
||||
* @nformats: the length of the array of supported pixel formats
|
||||
*/
|
||||
struct pl111_variant_data {
|
||||
const char *name;
|
||||
bool is_pl110;
|
||||
const u32 *formats;
|
||||
unsigned int nformats;
|
||||
};
|
||||
|
||||
struct pl111_drm_dev_private {
|
||||
struct drm_device *drm;
|
||||
|
||||
struct pl111_drm_connector connector;
|
||||
struct drm_connector *connector;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
struct drm_simple_display_pipe pipe;
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
|
||||
void *regs;
|
||||
u32 ienb;
|
||||
u32 ctrl;
|
||||
/* The pixel clock (a reference to our clock divider off of CLCDCLK). */
|
||||
struct clk *clk;
|
||||
/* pl111's internal clock divider. */
|
||||
|
@ -48,20 +66,15 @@ struct pl111_drm_dev_private {
|
|||
* subsystem and pl111_display_enable().
|
||||
*/
|
||||
spinlock_t tim2_lock;
|
||||
const struct pl111_variant_data *variant;
|
||||
void (*variant_display_enable) (struct drm_device *drm, u32 format);
|
||||
void (*variant_display_disable) (struct drm_device *drm);
|
||||
};
|
||||
|
||||
#define to_pl111_connector(x) \
|
||||
container_of(x, struct pl111_drm_connector, connector)
|
||||
|
||||
int pl111_display_init(struct drm_device *dev);
|
||||
int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc);
|
||||
void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc);
|
||||
irqreturn_t pl111_irq(int irq, void *data);
|
||||
int pl111_connector_init(struct drm_device *dev);
|
||||
int pl111_encoder_init(struct drm_device *dev);
|
||||
int pl111_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
int pl111_debugfs_init(struct drm_minor *minor);
|
||||
|
||||
#endif /* _PL111_DRM_H_ */
|
||||
|
|
|
@ -41,9 +41,6 @@
|
|||
* - Fix race between setting plane base address and getting IRQ for
|
||||
* vsync firing the pageflip completion.
|
||||
*
|
||||
* - Expose the correct set of formats we can support based on the
|
||||
* "arm,pl11x,tft-r0g0b0-pads" DT property.
|
||||
*
|
||||
* - Use the "max-memory-bandwidth" DT property to filter the
|
||||
* supported formats.
|
||||
*
|
||||
|
@ -68,8 +65,12 @@
|
|||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include "pl111_drm.h"
|
||||
#include "pl111_versatile.h"
|
||||
|
||||
#define DRIVER_DESC "DRM module for PL111"
|
||||
|
||||
|
@ -83,6 +84,8 @@ static int pl111_modeset_init(struct drm_device *dev)
|
|||
{
|
||||
struct drm_mode_config *mode_config;
|
||||
struct pl111_drm_dev_private *priv = dev->dev_private;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
int ret = 0;
|
||||
|
||||
drm_mode_config_init(dev);
|
||||
|
@ -93,34 +96,43 @@ static int pl111_modeset_init(struct drm_device *dev)
|
|||
mode_config->min_height = 1;
|
||||
mode_config->max_height = 768;
|
||||
|
||||
ret = pl111_connector_init(dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to create pl111_drm_connector\n");
|
||||
goto out_config;
|
||||
}
|
||||
|
||||
/* Don't actually attach if we didn't find a drm_panel
|
||||
* attached to us. This will allow a kernel to include both
|
||||
* the fbdev pl111 driver and this one, and choose between
|
||||
* them based on which subsystem has support for the panel.
|
||||
*/
|
||||
if (!priv->connector.panel) {
|
||||
dev_info(dev->dev,
|
||||
"Disabling due to lack of DRM panel device.\n");
|
||||
ret = -ENODEV;
|
||||
goto out_config;
|
||||
ret = drm_of_find_panel_or_bridge(dev->dev->of_node,
|
||||
0, 0, &panel, &bridge);
|
||||
if (ret && ret != -ENODEV)
|
||||
return ret;
|
||||
if (panel) {
|
||||
bridge = drm_panel_bridge_add(panel,
|
||||
DRM_MODE_CONNECTOR_Unknown);
|
||||
if (IS_ERR(bridge)) {
|
||||
ret = PTR_ERR(bridge);
|
||||
goto out_config;
|
||||
}
|
||||
/*
|
||||
* TODO: when we are using a different bridge than a panel
|
||||
* (such as a dumb VGA connector) we need to devise a different
|
||||
* method to get the connector out of the bridge.
|
||||
*/
|
||||
}
|
||||
|
||||
ret = pl111_display_init(dev);
|
||||
if (ret != 0) {
|
||||
dev_err(dev->dev, "Failed to init display\n");
|
||||
goto out_config;
|
||||
goto out_bridge;
|
||||
}
|
||||
|
||||
ret = drm_simple_display_pipe_attach_bridge(&priv->pipe,
|
||||
bridge);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->bridge = bridge;
|
||||
priv->panel = panel;
|
||||
priv->connector = panel->connector;
|
||||
|
||||
ret = drm_vblank_init(dev, 1);
|
||||
if (ret != 0) {
|
||||
dev_err(dev->dev, "Failed to init vblank\n");
|
||||
goto out_config;
|
||||
goto out_bridge;
|
||||
}
|
||||
|
||||
drm_mode_config_reset(dev);
|
||||
|
@ -132,6 +144,9 @@ static int pl111_modeset_init(struct drm_device *dev)
|
|||
|
||||
goto finish;
|
||||
|
||||
out_bridge:
|
||||
if (panel)
|
||||
drm_panel_bridge_remove(bridge);
|
||||
out_config:
|
||||
drm_mode_config_cleanup(dev);
|
||||
finish:
|
||||
|
@ -183,6 +198,7 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
|
|||
{
|
||||
struct device *dev = &amba_dev->dev;
|
||||
struct pl111_drm_dev_private *priv;
|
||||
struct pl111_variant_data *variant = id->data;
|
||||
struct drm_device *drm;
|
||||
int ret;
|
||||
|
||||
|
@ -196,6 +212,33 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
|
|||
amba_set_drvdata(amba_dev, drm);
|
||||
priv->drm = drm;
|
||||
drm->dev_private = priv;
|
||||
priv->variant = variant;
|
||||
|
||||
/*
|
||||
* The PL110 and PL111 variants have two registers
|
||||
* swapped: interrupt enable and control. For this reason
|
||||
* we use offsets that we can change per variant.
|
||||
*/
|
||||
if (variant->is_pl110) {
|
||||
/*
|
||||
* The ARM Versatile boards are even more special:
|
||||
* their PrimeCell ID say they are PL110 but the
|
||||
* control and interrupt enable registers are anyway
|
||||
* swapped to the PL111 order so they are not following
|
||||
* the PL110 datasheet.
|
||||
*/
|
||||
if (of_machine_is_compatible("arm,versatile-ab") ||
|
||||
of_machine_is_compatible("arm,versatile-pb")) {
|
||||
priv->ienb = CLCD_PL111_IENB;
|
||||
priv->ctrl = CLCD_PL111_CNTL;
|
||||
} else {
|
||||
priv->ienb = CLCD_PL110_IENB;
|
||||
priv->ctrl = CLCD_PL110_CNTL;
|
||||
}
|
||||
} else {
|
||||
priv->ienb = CLCD_PL111_IENB;
|
||||
priv->ctrl = CLCD_PL111_CNTL;
|
||||
}
|
||||
|
||||
priv->regs = devm_ioremap_resource(dev, &amba_dev->res);
|
||||
if (IS_ERR(priv->regs)) {
|
||||
|
@ -204,15 +247,19 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
|
|||
}
|
||||
|
||||
/* turn off interrupts before requesting the irq */
|
||||
writel(0, priv->regs + CLCD_PL111_IENB);
|
||||
writel(0, priv->regs + priv->ienb);
|
||||
|
||||
ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0,
|
||||
"pl111", priv);
|
||||
variant->name, priv);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "%s failed irq %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pl111_versatile_init(dev, priv);
|
||||
if (ret)
|
||||
goto dev_unref;
|
||||
|
||||
ret = pl111_modeset_init(drm);
|
||||
if (ret != 0)
|
||||
goto dev_unref;
|
||||
|
@ -236,16 +283,70 @@ static int pl111_amba_remove(struct amba_device *amba_dev)
|
|||
drm_dev_unregister(drm);
|
||||
if (priv->fbdev)
|
||||
drm_fbdev_cma_fini(priv->fbdev);
|
||||
if (priv->panel)
|
||||
drm_panel_bridge_remove(priv->bridge);
|
||||
drm_mode_config_cleanup(drm);
|
||||
drm_dev_unref(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amba_id pl111_id_table[] = {
|
||||
/*
|
||||
* This variant exist in early versions like the ARM Integrator
|
||||
* and this version lacks the 565 and 444 pixel formats.
|
||||
*/
|
||||
static const u32 pl110_pixel_formats[] = {
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_ABGR1555,
|
||||
DRM_FORMAT_XBGR1555,
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_XRGB1555,
|
||||
};
|
||||
|
||||
static const struct pl111_variant_data pl110_variant = {
|
||||
.name = "PL110",
|
||||
.is_pl110 = true,
|
||||
.formats = pl110_pixel_formats,
|
||||
.nformats = ARRAY_SIZE(pl110_pixel_formats),
|
||||
};
|
||||
|
||||
/* RealView, Versatile Express etc use this modern variant */
|
||||
static const u32 pl111_pixel_formats[] = {
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_BGR565,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_ABGR1555,
|
||||
DRM_FORMAT_XBGR1555,
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_XRGB1555,
|
||||
DRM_FORMAT_ABGR4444,
|
||||
DRM_FORMAT_XBGR4444,
|
||||
DRM_FORMAT_ARGB4444,
|
||||
DRM_FORMAT_XRGB4444,
|
||||
};
|
||||
|
||||
static const struct pl111_variant_data pl111_variant = {
|
||||
.name = "PL111",
|
||||
.formats = pl111_pixel_formats,
|
||||
.nformats = ARRAY_SIZE(pl111_pixel_formats),
|
||||
};
|
||||
|
||||
static const struct amba_id pl111_id_table[] = {
|
||||
{
|
||||
.id = 0x00041110,
|
||||
.mask = 0x000fffff,
|
||||
.data = (void*)&pl110_variant,
|
||||
},
|
||||
{
|
||||
.id = 0x00041111,
|
||||
.mask = 0x000fffff,
|
||||
.data = (void*)&pl111_variant,
|
||||
},
|
||||
{0, 0},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "pl111_versatile.h"
|
||||
#include "pl111_drm.h"
|
||||
|
||||
static struct regmap *versatile_syscon_map;
|
||||
|
||||
/*
|
||||
* We detect the different syscon types from the compatible strings.
|
||||
*/
|
||||
enum versatile_clcd {
|
||||
INTEGRATOR_CLCD_CM,
|
||||
VERSATILE_CLCD,
|
||||
REALVIEW_CLCD_EB,
|
||||
REALVIEW_CLCD_PB1176,
|
||||
REALVIEW_CLCD_PB11MP,
|
||||
REALVIEW_CLCD_PBA8,
|
||||
REALVIEW_CLCD_PBX,
|
||||
};
|
||||
|
||||
static const struct of_device_id versatile_clcd_of_match[] = {
|
||||
{
|
||||
.compatible = "arm,core-module-integrator",
|
||||
.data = (void *)INTEGRATOR_CLCD_CM,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,versatile-sysreg",
|
||||
.data = (void *)VERSATILE_CLCD,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-eb-syscon",
|
||||
.data = (void *)REALVIEW_CLCD_EB,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-pb1176-syscon",
|
||||
.data = (void *)REALVIEW_CLCD_PB1176,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-pb11mp-syscon",
|
||||
.data = (void *)REALVIEW_CLCD_PB11MP,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-pba8-syscon",
|
||||
.data = (void *)REALVIEW_CLCD_PBA8,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-pbx-syscon",
|
||||
.data = (void *)REALVIEW_CLCD_PBX,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
/*
|
||||
* Core module CLCD control on the Integrator/CP, bits
|
||||
* 8 thru 19 of the CM_CONTROL register controls a bunch
|
||||
* of CLCD settings.
|
||||
*/
|
||||
#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C
|
||||
#define INTEGRATOR_CLCD_LCDBIASEN BIT(8)
|
||||
#define INTEGRATOR_CLCD_LCDBIASUP BIT(9)
|
||||
#define INTEGRATOR_CLCD_LCDBIASDN BIT(10)
|
||||
/* Bits 11,12,13 controls the LCD type */
|
||||
#define INTEGRATOR_CLCD_LCDMUX_MASK (BIT(11)|BIT(12)|BIT(13))
|
||||
#define INTEGRATOR_CLCD_LCDMUX_LCD24 BIT(11)
|
||||
#define INTEGRATOR_CLCD_LCDMUX_VGA565 BIT(12)
|
||||
#define INTEGRATOR_CLCD_LCDMUX_SHARP (BIT(11)|BIT(12))
|
||||
#define INTEGRATOR_CLCD_LCDMUX_VGA555 BIT(13)
|
||||
#define INTEGRATOR_CLCD_LCDMUX_VGA24 (BIT(11)|BIT(12)|BIT(13))
|
||||
#define INTEGRATOR_CLCD_LCD0_EN BIT(14)
|
||||
#define INTEGRATOR_CLCD_LCD1_EN BIT(15)
|
||||
/* R/L flip on Sharp */
|
||||
#define INTEGRATOR_CLCD_LCD_STATIC1 BIT(16)
|
||||
/* U/D flip on Sharp */
|
||||
#define INTEGRATOR_CLCD_LCD_STATIC2 BIT(17)
|
||||
/* No connection on Sharp */
|
||||
#define INTEGRATOR_CLCD_LCD_STATIC BIT(18)
|
||||
/* 0 = 24bit VGA, 1 = 18bit VGA */
|
||||
#define INTEGRATOR_CLCD_LCD_N24BITEN BIT(19)
|
||||
|
||||
#define INTEGRATOR_CLCD_MASK (INTEGRATOR_CLCD_LCDBIASEN | \
|
||||
INTEGRATOR_CLCD_LCDBIASUP | \
|
||||
INTEGRATOR_CLCD_LCDBIASDN | \
|
||||
INTEGRATOR_CLCD_LCDMUX_MASK | \
|
||||
INTEGRATOR_CLCD_LCD0_EN | \
|
||||
INTEGRATOR_CLCD_LCD1_EN | \
|
||||
INTEGRATOR_CLCD_LCD_STATIC1 | \
|
||||
INTEGRATOR_CLCD_LCD_STATIC2 | \
|
||||
INTEGRATOR_CLCD_LCD_STATIC | \
|
||||
INTEGRATOR_CLCD_LCD_N24BITEN)
|
||||
|
||||
static void pl111_integrator_enable(struct drm_device *drm, u32 format)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
dev_info(drm->dev, "enable Integrator CLCD connectors\n");
|
||||
|
||||
/* FIXME: really needed? */
|
||||
val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 |
|
||||
INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN;
|
||||
|
||||
switch (format) {
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
break;
|
||||
case DRM_FORMAT_BGR565:
|
||||
case DRM_FORMAT_RGB565:
|
||||
/* truecolor RGB565 */
|
||||
val |= INTEGRATOR_CLCD_LCDMUX_VGA565;
|
||||
break;
|
||||
case DRM_FORMAT_XBGR1555:
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
/* Pseudocolor, RGB555, BGR555 */
|
||||
val |= INTEGRATOR_CLCD_LCDMUX_VGA555;
|
||||
break;
|
||||
default:
|
||||
dev_err(drm->dev, "unhandled format on Integrator 0x%08x\n",
|
||||
format);
|
||||
break;
|
||||
}
|
||||
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
INTEGRATOR_HDR_CTRL_OFFSET,
|
||||
INTEGRATOR_CLCD_MASK,
|
||||
val);
|
||||
}
|
||||
|
||||
/*
|
||||
* This configuration register in the Versatile and RealView
|
||||
* family is uniformly present but appears more and more
|
||||
* unutilized starting with the RealView series.
|
||||
*/
|
||||
#define SYS_CLCD 0x50
|
||||
#define SYS_CLCD_MODE_MASK (BIT(0)|BIT(1))
|
||||
#define SYS_CLCD_MODE_888 0
|
||||
#define SYS_CLCD_MODE_5551 BIT(0)
|
||||
#define SYS_CLCD_MODE_565_R_LSB BIT(1)
|
||||
#define SYS_CLCD_MODE_565_B_LSB (BIT(0)|BIT(1))
|
||||
#define SYS_CLCD_CONNECTOR_MASK (BIT(2)|BIT(3)|BIT(4)|BIT(5))
|
||||
#define SYS_CLCD_NLCDIOON BIT(2)
|
||||
#define SYS_CLCD_VDDPOSSWITCH BIT(3)
|
||||
#define SYS_CLCD_PWR3V5SWITCH BIT(4)
|
||||
#define SYS_CLCD_VDDNEGSWITCH BIT(5)
|
||||
|
||||
static void pl111_versatile_disable(struct drm_device *drm)
|
||||
{
|
||||
dev_info(drm->dev, "disable Versatile CLCD connectors\n");
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
SYS_CLCD,
|
||||
SYS_CLCD_CONNECTOR_MASK,
|
||||
0);
|
||||
}
|
||||
|
||||
static void pl111_versatile_enable(struct drm_device *drm, u32 format)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
dev_info(drm->dev, "enable Versatile CLCD connectors\n");
|
||||
|
||||
switch (format) {
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
val |= SYS_CLCD_MODE_888;
|
||||
break;
|
||||
case DRM_FORMAT_BGR565:
|
||||
val |= SYS_CLCD_MODE_565_R_LSB;
|
||||
break;
|
||||
case DRM_FORMAT_RGB565:
|
||||
val |= SYS_CLCD_MODE_565_B_LSB;
|
||||
break;
|
||||
case DRM_FORMAT_ABGR1555:
|
||||
case DRM_FORMAT_XBGR1555:
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
val |= SYS_CLCD_MODE_5551;
|
||||
break;
|
||||
default:
|
||||
dev_err(drm->dev, "unhandled format on Versatile 0x%08x\n",
|
||||
format);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set up the MUX */
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
SYS_CLCD,
|
||||
SYS_CLCD_MODE_MASK,
|
||||
val);
|
||||
|
||||
/* Then enable the display */
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
SYS_CLCD,
|
||||
SYS_CLCD_CONNECTOR_MASK,
|
||||
SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
|
||||
}
|
||||
|
||||
static void pl111_realview_clcd_disable(struct drm_device *drm)
|
||||
{
|
||||
dev_info(drm->dev, "disable RealView CLCD connectors\n");
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
SYS_CLCD,
|
||||
SYS_CLCD_CONNECTOR_MASK,
|
||||
0);
|
||||
}
|
||||
|
||||
static void pl111_realview_clcd_enable(struct drm_device *drm, u32 format)
|
||||
{
|
||||
dev_info(drm->dev, "enable RealView CLCD connectors\n");
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
SYS_CLCD,
|
||||
SYS_CLCD_CONNECTOR_MASK,
|
||||
SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
|
||||
}
|
||||
|
||||
int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
|
||||
{
|
||||
const struct of_device_id *clcd_id;
|
||||
enum versatile_clcd versatile_clcd_type;
|
||||
struct device_node *np;
|
||||
struct regmap *map;
|
||||
|
||||
np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match,
|
||||
&clcd_id);
|
||||
if (!np) {
|
||||
/* Non-ARM reference designs, just bail out */
|
||||
return 0;
|
||||
}
|
||||
versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
|
||||
|
||||
map = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(map)) {
|
||||
dev_err(dev, "no Versatile syscon regmap\n");
|
||||
return PTR_ERR(map);
|
||||
}
|
||||
|
||||
switch (versatile_clcd_type) {
|
||||
case INTEGRATOR_CLCD_CM:
|
||||
versatile_syscon_map = map;
|
||||
priv->variant_display_enable = pl111_integrator_enable;
|
||||
dev_info(dev, "set up callbacks for Integrator PL110\n");
|
||||
break;
|
||||
case VERSATILE_CLCD:
|
||||
versatile_syscon_map = map;
|
||||
priv->variant_display_enable = pl111_versatile_enable;
|
||||
priv->variant_display_disable = pl111_versatile_disable;
|
||||
dev_info(dev, "set up callbacks for Versatile PL110+\n");
|
||||
break;
|
||||
case REALVIEW_CLCD_EB:
|
||||
case REALVIEW_CLCD_PB1176:
|
||||
case REALVIEW_CLCD_PB11MP:
|
||||
case REALVIEW_CLCD_PBA8:
|
||||
case REALVIEW_CLCD_PBX:
|
||||
versatile_syscon_map = map;
|
||||
priv->variant_display_enable = pl111_realview_clcd_enable;
|
||||
priv->variant_display_disable = pl111_realview_clcd_disable;
|
||||
dev_info(dev, "set up callbacks for RealView PL111\n");
|
||||
break;
|
||||
default:
|
||||
dev_info(dev, "unknown Versatile system controller\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pl111_versatile_init);
|
|
@ -0,0 +1,9 @@
|
|||
#include <linux/device.h>
|
||||
#include "pl111_drm.h"
|
||||
|
||||
#ifndef PL111_VERSATILE_H
|
||||
#define PL111_VERSATILE_H
|
||||
|
||||
int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv);
|
||||
|
||||
#endif
|
|
@ -57,4 +57,12 @@ config ROCKCHIP_INNO_HDMI
|
|||
for the Innosilicon HDMI driver. If you want to enable
|
||||
HDMI on RK3036 based SoC, you should select this option.
|
||||
|
||||
config ROCKCHIP_LVDS
|
||||
bool "Rockchip LVDS support"
|
||||
depends on DRM_ROCKCHIP
|
||||
help
|
||||
Choose this option to enable support for Rockchip LVDS controllers.
|
||||
Rockchip rk3288 SoC has LVDS TX Controller can be used, and it
|
||||
support LVDS, rgb, dual LVDS output mode. say Y to enable its
|
||||
driver.
|
||||
endif
|
||||
|
|
|
@ -12,5 +12,6 @@ rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
|
|||
rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
|
||||
|
||||
obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
|
||||
|
|
|
@ -88,7 +88,7 @@ static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
|
|||
if (!analogix_dp_psr_supported(dp->dev))
|
||||
return;
|
||||
|
||||
dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
|
||||
DRM_DEV_DEBUG(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
|
||||
|
||||
spin_lock_irqsave(&dp->psr_lock, flags);
|
||||
if (enabled)
|
||||
|
@ -110,7 +110,7 @@ static void analogix_dp_psr_work(struct work_struct *work)
|
|||
ret = rockchip_drm_wait_vact_end(dp->encoder.crtc,
|
||||
PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
|
||||
if (ret) {
|
||||
dev_err(dp->dev, "line flag interrupt did not arrive\n");
|
||||
DRM_DEV_ERROR(dp->dev, "line flag interrupt did not arrive\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -140,13 +140,13 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
|
|||
|
||||
ret = clk_prepare_enable(dp->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "failed to enable pclk %d\n", ret);
|
||||
DRM_DEV_ERROR(dp->dev, "failed to enable pclk %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rockchip_dp_pre_init(dp);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "failed to dp pre init %d\n", ret);
|
||||
DRM_DEV_ERROR(dp->dev, "failed to dp pre init %d\n", ret);
|
||||
clk_disable_unprepare(dp->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
@ -211,17 +211,17 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder)
|
|||
else
|
||||
val = dp->data->lcdsel_big;
|
||||
|
||||
dev_dbg(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
|
||||
DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
|
||||
|
||||
ret = clk_prepare_enable(dp->grfclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "failed to enable grfclk %d\n", ret);
|
||||
DRM_DEV_ERROR(dp->dev, "failed to enable grfclk %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = regmap_write(dp->grf, dp->data->lcdsel_grf_reg, val);
|
||||
if (ret != 0)
|
||||
dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
|
||||
DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret);
|
||||
|
||||
clk_disable_unprepare(dp->grfclk);
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ static int rockchip_dp_init(struct rockchip_dp_device *dp)
|
|||
|
||||
dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
if (IS_ERR(dp->grf)) {
|
||||
dev_err(dev, "failed to get rockchip,grf property\n");
|
||||
DRM_DEV_ERROR(dev, "failed to get rockchip,grf property\n");
|
||||
return PTR_ERR(dp->grf);
|
||||
}
|
||||
|
||||
|
@ -287,31 +287,31 @@ static int rockchip_dp_init(struct rockchip_dp_device *dp)
|
|||
} else if (PTR_ERR(dp->grfclk) == -EPROBE_DEFER) {
|
||||
return -EPROBE_DEFER;
|
||||
} else if (IS_ERR(dp->grfclk)) {
|
||||
dev_err(dev, "failed to get grf clock\n");
|
||||
DRM_DEV_ERROR(dev, "failed to get grf clock\n");
|
||||
return PTR_ERR(dp->grfclk);
|
||||
}
|
||||
|
||||
dp->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(dp->pclk)) {
|
||||
dev_err(dev, "failed to get pclk property\n");
|
||||
DRM_DEV_ERROR(dev, "failed to get pclk property\n");
|
||||
return PTR_ERR(dp->pclk);
|
||||
}
|
||||
|
||||
dp->rst = devm_reset_control_get(dev, "dp");
|
||||
if (IS_ERR(dp->rst)) {
|
||||
dev_err(dev, "failed to get dp reset control\n");
|
||||
DRM_DEV_ERROR(dev, "failed to get dp reset control\n");
|
||||
return PTR_ERR(dp->rst);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(dp->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "failed to enable pclk %d\n", ret);
|
||||
DRM_DEV_ERROR(dp->dev, "failed to enable pclk %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rockchip_dp_pre_init(dp);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "failed to pre init %d\n", ret);
|
||||
DRM_DEV_ERROR(dp->dev, "failed to pre init %d\n", ret);
|
||||
clk_disable_unprepare(dp->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -323,7 +323,7 @@ int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
|
|||
reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
|
||||
dp->fw_version |= reg << 24;
|
||||
|
||||
dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
|
||||
DRM_DEV_DEBUG(dp->dev, "firmware version: %x\n", dp->fw_version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -430,9 +430,9 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
|
|||
|
||||
testdin = max_mbps_to_testdin(dsi->lane_mbps);
|
||||
if (testdin < 0) {
|
||||
dev_err(dsi->dev,
|
||||
"failed to get testdin for %dmbps lane clock\n",
|
||||
dsi->lane_mbps);
|
||||
DRM_DEV_ERROR(dsi->dev,
|
||||
"failed to get testdin for %dmbps lane clock\n",
|
||||
dsi->lane_mbps);
|
||||
return testdin;
|
||||
}
|
||||
|
||||
|
@ -443,7 +443,7 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
|
|||
|
||||
ret = clk_prepare_enable(dsi->phy_cfg_clk);
|
||||
if (ret) {
|
||||
dev_err(dsi->dev, "Failed to enable phy_cfg_clk\n");
|
||||
DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -501,7 +501,7 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
|
|||
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
|
||||
val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(dsi->dev, "failed to wait for phy lock state\n");
|
||||
DRM_DEV_ERROR(dsi->dev, "failed to wait for phy lock state\n");
|
||||
goto phy_init_end;
|
||||
}
|
||||
|
||||
|
@ -509,8 +509,8 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
|
|||
val, val & STOP_STATE_CLK_LANE, 1000,
|
||||
PHY_STATUS_TIMEOUT_US);
|
||||
if (ret < 0)
|
||||
dev_err(dsi->dev,
|
||||
"failed to wait for phy clk lane stop state\n");
|
||||
DRM_DEV_ERROR(dsi->dev,
|
||||
"failed to wait for phy clk lane stop state\n");
|
||||
|
||||
phy_init_end:
|
||||
clk_disable_unprepare(dsi->phy_cfg_clk);
|
||||
|
@ -529,8 +529,9 @@ static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi,
|
|||
|
||||
bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
|
||||
if (bpp < 0) {
|
||||
dev_err(dsi->dev, "failed to get bpp for pixel format %d\n",
|
||||
dsi->format);
|
||||
DRM_DEV_ERROR(dsi->dev,
|
||||
"failed to get bpp for pixel format %d\n",
|
||||
dsi->format);
|
||||
return bpp;
|
||||
}
|
||||
|
||||
|
@ -541,7 +542,8 @@ static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi,
|
|||
if (tmp < max_mbps)
|
||||
target_mbps = tmp;
|
||||
else
|
||||
dev_err(dsi->dev, "DPHY clock frequency is out of range\n");
|
||||
DRM_DEV_ERROR(dsi->dev,
|
||||
"DPHY clock frequency is out of range\n");
|
||||
}
|
||||
|
||||
pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC);
|
||||
|
@ -582,8 +584,9 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
|
|||
struct dw_mipi_dsi *dsi = host_to_dsi(host);
|
||||
|
||||
if (device->lanes > dsi->pdata->max_data_lanes) {
|
||||
dev_err(dsi->dev, "the number of data lanes(%u) is too many\n",
|
||||
device->lanes);
|
||||
DRM_DEV_ERROR(dsi->dev,
|
||||
"the number of data lanes(%u) is too many\n",
|
||||
device->lanes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -632,7 +635,8 @@ static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
|
|||
val, !(val & GEN_CMD_FULL), 1000,
|
||||
CMD_PKT_STATUS_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(dsi->dev, "failed to get available command FIFO\n");
|
||||
DRM_DEV_ERROR(dsi->dev,
|
||||
"failed to get available command FIFO\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -643,7 +647,7 @@ static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
|
|||
val, (val & mask) == mask,
|
||||
1000, CMD_PKT_STATUS_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(dsi->dev, "failed to write command FIFO\n");
|
||||
DRM_DEV_ERROR(dsi->dev, "failed to write command FIFO\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -663,8 +667,9 @@ static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi,
|
|||
data |= tx_buf[1] << 8;
|
||||
|
||||
if (msg->tx_len > 2) {
|
||||
dev_err(dsi->dev, "too long tx buf length %zu for short write\n",
|
||||
msg->tx_len);
|
||||
DRM_DEV_ERROR(dsi->dev,
|
||||
"too long tx buf length %zu for short write\n",
|
||||
msg->tx_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -682,8 +687,9 @@ static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
|
|||
u32 val;
|
||||
|
||||
if (msg->tx_len < 3) {
|
||||
dev_err(dsi->dev, "wrong tx buf length %zu for long write\n",
|
||||
msg->tx_len);
|
||||
DRM_DEV_ERROR(dsi->dev,
|
||||
"wrong tx buf length %zu for long write\n",
|
||||
msg->tx_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -704,8 +710,8 @@ static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
|
|||
val, !(val & GEN_PLD_W_FULL), 1000,
|
||||
CMD_PKT_STATUS_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(dsi->dev,
|
||||
"failed to get available write payload FIFO\n");
|
||||
DRM_DEV_ERROR(dsi->dev,
|
||||
"failed to get available write payload FIFO\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -731,8 +737,8 @@ static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
|
|||
ret = dw_mipi_dsi_dcs_long_write(dsi, msg);
|
||||
break;
|
||||
default:
|
||||
dev_err(dsi->dev, "unsupported message type 0x%02x\n",
|
||||
msg->type);
|
||||
DRM_DEV_ERROR(dsi->dev, "unsupported message type 0x%02x\n",
|
||||
msg->type);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -935,7 +941,7 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
|
|||
return;
|
||||
|
||||
if (clk_prepare_enable(dsi->pclk)) {
|
||||
dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
|
||||
DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -967,7 +973,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
|
|||
return;
|
||||
|
||||
if (clk_prepare_enable(dsi->pclk)) {
|
||||
dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
|
||||
DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -991,7 +997,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
|
|||
*/
|
||||
ret = clk_prepare_enable(dsi->grf_clk);
|
||||
if (ret) {
|
||||
dev_err(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
|
||||
DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1004,7 +1010,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
|
|||
|
||||
dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
|
||||
if (drm_panel_prepare(dsi->panel))
|
||||
dev_err(dsi->dev, "failed to prepare panel\n");
|
||||
DRM_DEV_ERROR(dsi->dev, "failed to prepare panel\n");
|
||||
|
||||
dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
|
||||
drm_panel_enable(dsi->panel);
|
||||
|
@ -1017,7 +1023,8 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
|
|||
val = pdata->dsi0_en_bit << 16;
|
||||
|
||||
regmap_write(dsi->grf_regmap, pdata->grf_switch_reg, val);
|
||||
dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
|
||||
DRM_DEV_DEBUG(dsi->dev,
|
||||
"vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
|
||||
dsi->dpms_mode = DRM_MODE_DPMS_ON;
|
||||
|
||||
clk_disable_unprepare(dsi->grf_clk);
|
||||
|
@ -1111,7 +1118,7 @@ static int dw_mipi_dsi_register(struct drm_device *drm,
|
|||
ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,
|
||||
DRM_MODE_ENCODER_DSI, NULL);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize encoder with drm\n");
|
||||
DRM_DEV_ERROR(dev, "Failed to initialize encoder with drm\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1133,7 +1140,7 @@ static int rockchip_mipi_parse_dt(struct dw_mipi_dsi *dsi)
|
|||
|
||||
dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
if (IS_ERR(dsi->grf_regmap)) {
|
||||
dev_err(dsi->dev, "Unable to get rockchip,grf\n");
|
||||
DRM_DEV_ERROR(dsi->dev, "Unable to get rockchip,grf\n");
|
||||
return PTR_ERR(dsi->grf_regmap);
|
||||
}
|
||||
|
||||
|
@ -1205,14 +1212,15 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
|
|||
dsi->pllref_clk = devm_clk_get(dev, "ref");
|
||||
if (IS_ERR(dsi->pllref_clk)) {
|
||||
ret = PTR_ERR(dsi->pllref_clk);
|
||||
dev_err(dev, "Unable to get pll reference clock: %d\n", ret);
|
||||
DRM_DEV_ERROR(dev,
|
||||
"Unable to get pll reference clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dsi->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(dsi->pclk)) {
|
||||
ret = PTR_ERR(dsi->pclk);
|
||||
dev_err(dev, "Unable to get pclk: %d\n", ret);
|
||||
DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1226,7 +1234,8 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
|
|||
if (ret == -ENOENT) {
|
||||
apb_rst = NULL;
|
||||
} else {
|
||||
dev_err(dev, "Unable to get reset control: %d\n", ret);
|
||||
DRM_DEV_ERROR(dev,
|
||||
"Unable to get reset control: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -1234,7 +1243,7 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
|
|||
if (apb_rst) {
|
||||
ret = clk_prepare_enable(dsi->pclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to enable pclk\n", __func__);
|
||||
DRM_DEV_ERROR(dev, "Failed to enable pclk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1249,7 +1258,8 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
|
|||
dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg");
|
||||
if (IS_ERR(dsi->phy_cfg_clk)) {
|
||||
ret = PTR_ERR(dsi->phy_cfg_clk);
|
||||
dev_err(dev, "Unable to get phy_cfg_clk: %d\n", ret);
|
||||
DRM_DEV_ERROR(dev,
|
||||
"Unable to get phy_cfg_clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -1258,20 +1268,20 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
|
|||
dsi->grf_clk = devm_clk_get(dev, "grf");
|
||||
if (IS_ERR(dsi->grf_clk)) {
|
||||
ret = PTR_ERR(dsi->grf_clk);
|
||||
dev_err(dev, "Unable to get grf_clk: %d\n", ret);
|
||||
DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(dsi->pllref_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__);
|
||||
DRM_DEV_ERROR(dev, "Failed to enable pllref_clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dw_mipi_dsi_register(drm, dsi);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register mipi_dsi: %d\n", ret);
|
||||
DRM_DEV_ERROR(dev, "Failed to register mipi_dsi: %d\n", ret);
|
||||
goto err_pllref;
|
||||
}
|
||||
|
||||
|
@ -1281,7 +1291,7 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
|
|||
dsi->dsi_host.dev = dev;
|
||||
ret = mipi_dsi_host_register(&dsi->dsi_host);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register MIPI host: %d\n", ret);
|
||||
DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret);
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
|||
|
||||
hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
if (IS_ERR(hdmi->regmap)) {
|
||||
dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
|
||||
DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n");
|
||||
return PTR_ERR(hdmi->regmap);
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
|||
} else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
|
||||
return -EPROBE_DEFER;
|
||||
} else if (IS_ERR(hdmi->vpll_clk)) {
|
||||
dev_err(hdmi->dev, "failed to get grf clock\n");
|
||||
DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
|
||||
return PTR_ERR(hdmi->vpll_clk);
|
||||
}
|
||||
|
||||
|
@ -188,13 +188,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
|||
} else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
|
||||
return -EPROBE_DEFER;
|
||||
} else if (IS_ERR(hdmi->grf_clk)) {
|
||||
dev_err(hdmi->dev, "failed to get grf clock\n");
|
||||
DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
|
||||
return PTR_ERR(hdmi->grf_clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->vpll_clk);
|
||||
if (ret) {
|
||||
dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret);
|
||||
DRM_DEV_ERROR(hdmi->dev,
|
||||
"Failed to enable HDMI vpll: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -259,17 +260,17 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
|
|||
|
||||
ret = clk_prepare_enable(hdmi->grf_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(hdmi->dev, "failed to enable grfclk %d\n", ret);
|
||||
DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
|
||||
if (ret != 0)
|
||||
dev_err(hdmi->dev, "Could not write to GRF: %d\n", ret);
|
||||
DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret);
|
||||
|
||||
clk_disable_unprepare(hdmi->grf_clk);
|
||||
dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
|
||||
ret ? "LIT" : "BIG");
|
||||
DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n",
|
||||
ret ? "LIT" : "BIG");
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -368,7 +369,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
|||
|
||||
ret = rockchip_hdmi_parse_dt(hdmi);
|
||||
if (ret) {
|
||||
dev_err(hdmi->dev, "Unable to parse OF data\n");
|
||||
DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
|
|||
break;
|
||||
|
||||
default:
|
||||
dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
|
||||
DRM_DEV_ERROR(hdmi->dev, "Unknown power mode %d\n", mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -742,8 +742,9 @@ static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
|
|||
hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
|
||||
i + 1, num, msgs[i].len, msgs[i].flags);
|
||||
DRM_DEV_DEBUG(hdmi->dev,
|
||||
"xfer: num: %d/%d, len: %d, flags: %#x\n",
|
||||
i + 1, num, msgs[i].len, msgs[i].flags);
|
||||
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
|
||||
|
@ -806,7 +807,7 @@ static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
|
|||
|
||||
hdmi->i2c = i2c;
|
||||
|
||||
dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
|
||||
DRM_DEV_INFO(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
|
||||
|
||||
return adap;
|
||||
}
|
||||
|
@ -838,13 +839,14 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
|
|||
|
||||
hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
|
||||
if (IS_ERR(hdmi->pclk)) {
|
||||
dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
|
||||
DRM_DEV_ERROR(hdmi->dev, "Unable to get HDMI pclk clk\n");
|
||||
return PTR_ERR(hdmi->pclk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->pclk);
|
||||
if (ret) {
|
||||
dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
|
||||
DRM_DEV_ERROR(hdmi->dev,
|
||||
"Cannot enable HDMI pclk clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
|
|||
|
||||
ret = iommu_attach_device(private->domain, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to attach iommu device\n");
|
||||
DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -373,8 +373,9 @@ static int rockchip_drm_platform_of_probe(struct device *dev)
|
|||
|
||||
iommu = of_parse_phandle(port->parent, "iommus", 0);
|
||||
if (!iommu || !of_device_is_available(iommu->parent)) {
|
||||
dev_dbg(dev, "no iommu attached for %pOF, using non-iommu buffers\n",
|
||||
port->parent);
|
||||
DRM_DEV_DEBUG(dev,
|
||||
"no iommu attached for %pOF, using non-iommu buffers\n",
|
||||
port->parent);
|
||||
/*
|
||||
* if there is a crtc not support iommu, force set all
|
||||
* crtc use non-iommu buffer.
|
||||
|
@ -389,12 +390,13 @@ static int rockchip_drm_platform_of_probe(struct device *dev)
|
|||
}
|
||||
|
||||
if (i == 0) {
|
||||
dev_err(dev, "missing 'ports' property\n");
|
||||
DRM_DEV_ERROR(dev, "missing 'ports' property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
dev_err(dev, "No available vop found for display-subsystem.\n");
|
||||
DRM_DEV_ERROR(dev,
|
||||
"No available vop found for display-subsystem.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -453,6 +455,8 @@ static int __init rockchip_drm_init(void)
|
|||
|
||||
num_rockchip_sub_drivers = 0;
|
||||
ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP);
|
||||
ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
|
||||
CONFIG_ROCKCHIP_LVDS);
|
||||
ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
|
||||
CONFIG_ROCKCHIP_ANALOGIX_DP);
|
||||
ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
|
||||
|
|
|
@ -69,5 +69,6 @@ extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
|
|||
extern struct platform_driver dw_mipi_dsi_driver;
|
||||
extern struct platform_driver inno_hdmi_driver;
|
||||
extern struct platform_driver rockchip_dp_driver;
|
||||
extern struct platform_driver rockchip_lvds_driver;
|
||||
extern struct platform_driver vop_platform_driver;
|
||||
#endif /* _ROCKCHIP_DRM_DRV_H_ */
|
||||
|
|
|
@ -100,8 +100,9 @@ rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cm
|
|||
ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
|
||||
&rockchip_drm_fb_funcs);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
|
||||
ret);
|
||||
DRM_DEV_ERROR(dev->dev,
|
||||
"Failed to initialize framebuffer: %d\n",
|
||||
ret);
|
||||
kfree(rockchip_fb);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
@ -134,7 +135,8 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
|
|||
|
||||
obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
|
||||
if (!obj) {
|
||||
dev_err(dev->dev, "Failed to lookup GEM object\n");
|
||||
DRM_DEV_ERROR(dev->dev,
|
||||
"Failed to lookup GEM object\n");
|
||||
ret = -ENXIO;
|
||||
goto err_gem_object_unreference;
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
|
|||
|
||||
fbi = drm_fb_helper_alloc_fbi(helper);
|
||||
if (IS_ERR(fbi)) {
|
||||
dev_err(dev->dev, "Failed to create framebuffer info.\n");
|
||||
DRM_DEV_ERROR(dev->dev, "Failed to create framebuffer info.\n");
|
||||
ret = PTR_ERR(fbi);
|
||||
goto out;
|
||||
}
|
||||
|
@ -84,7 +84,8 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
|
|||
helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd,
|
||||
private->fbdev_bo);
|
||||
if (IS_ERR(helper->fb)) {
|
||||
dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
|
||||
DRM_DEV_ERROR(dev->dev,
|
||||
"Failed to allocate DRM framebuffer.\n");
|
||||
ret = PTR_ERR(helper->fb);
|
||||
goto out;
|
||||
}
|
||||
|
@ -138,21 +139,24 @@ int rockchip_drm_fbdev_init(struct drm_device *dev)
|
|||
|
||||
ret = drm_fb_helper_init(dev, helper, ROCKCHIP_MAX_CONNECTOR);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n",
|
||||
ret);
|
||||
DRM_DEV_ERROR(dev->dev,
|
||||
"Failed to initialize drm fb helper - %d.\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_fb_helper_single_add_all_connectors(helper);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to add connectors - %d.\n", ret);
|
||||
DRM_DEV_ERROR(dev->dev,
|
||||
"Failed to add connectors - %d.\n", ret);
|
||||
goto err_drm_fb_helper_fini;
|
||||
}
|
||||
|
||||
ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to set initial hw config - %d.\n",
|
||||
ret);
|
||||
DRM_DEV_ERROR(dev->dev,
|
||||
"Failed to set initial hw config - %d.\n",
|
||||
ret);
|
||||
goto err_drm_fb_helper_fini;
|
||||
}
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ static void vop_reg_set(struct vop *vop, const struct vop_reg *reg,
|
|||
int offset, mask, shift;
|
||||
|
||||
if (!reg || !reg->mask) {
|
||||
dev_dbg(vop->dev, "Warning: not support %s\n", reg_name);
|
||||
DRM_DEV_DEBUG(vop->dev, "Warning: not support %s\n", reg_name);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -499,7 +499,7 @@ static int vop_enable(struct drm_crtc *crtc)
|
|||
|
||||
ret = pm_runtime_get_sync(vop->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
|
||||
DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -523,7 +523,8 @@ static int vop_enable(struct drm_crtc *crtc)
|
|||
*/
|
||||
ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev);
|
||||
if (ret) {
|
||||
dev_err(vop->dev, "failed to attach dma mapping, %d\n", ret);
|
||||
DRM_DEV_ERROR(vop->dev,
|
||||
"failed to attach dma mapping, %d\n", ret);
|
||||
goto err_disable_aclk;
|
||||
}
|
||||
|
||||
|
@ -1361,42 +1362,42 @@ static int vop_initial(struct vop *vop)
|
|||
|
||||
vop->hclk = devm_clk_get(vop->dev, "hclk_vop");
|
||||
if (IS_ERR(vop->hclk)) {
|
||||
dev_err(vop->dev, "failed to get hclk source\n");
|
||||
DRM_DEV_ERROR(vop->dev, "failed to get hclk source\n");
|
||||
return PTR_ERR(vop->hclk);
|
||||
}
|
||||
vop->aclk = devm_clk_get(vop->dev, "aclk_vop");
|
||||
if (IS_ERR(vop->aclk)) {
|
||||
dev_err(vop->dev, "failed to get aclk source\n");
|
||||
DRM_DEV_ERROR(vop->dev, "failed to get aclk source\n");
|
||||
return PTR_ERR(vop->aclk);
|
||||
}
|
||||
vop->dclk = devm_clk_get(vop->dev, "dclk_vop");
|
||||
if (IS_ERR(vop->dclk)) {
|
||||
dev_err(vop->dev, "failed to get dclk source\n");
|
||||
DRM_DEV_ERROR(vop->dev, "failed to get dclk source\n");
|
||||
return PTR_ERR(vop->dclk);
|
||||
}
|
||||
|
||||
ret = pm_runtime_get_sync(vop->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
|
||||
DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare(vop->dclk);
|
||||
if (ret < 0) {
|
||||
dev_err(vop->dev, "failed to prepare dclk\n");
|
||||
DRM_DEV_ERROR(vop->dev, "failed to prepare dclk\n");
|
||||
goto err_put_pm_runtime;
|
||||
}
|
||||
|
||||
/* Enable both the hclk and aclk to setup the vop */
|
||||
ret = clk_prepare_enable(vop->hclk);
|
||||
if (ret < 0) {
|
||||
dev_err(vop->dev, "failed to prepare/enable hclk\n");
|
||||
DRM_DEV_ERROR(vop->dev, "failed to prepare/enable hclk\n");
|
||||
goto err_unprepare_dclk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(vop->aclk);
|
||||
if (ret < 0) {
|
||||
dev_err(vop->dev, "failed to prepare/enable aclk\n");
|
||||
DRM_DEV_ERROR(vop->dev, "failed to prepare/enable aclk\n");
|
||||
goto err_disable_hclk;
|
||||
}
|
||||
|
||||
|
@ -1405,7 +1406,7 @@ static int vop_initial(struct vop *vop)
|
|||
*/
|
||||
ahb_rst = devm_reset_control_get(vop->dev, "ahb");
|
||||
if (IS_ERR(ahb_rst)) {
|
||||
dev_err(vop->dev, "failed to get ahb reset\n");
|
||||
DRM_DEV_ERROR(vop->dev, "failed to get ahb reset\n");
|
||||
ret = PTR_ERR(ahb_rst);
|
||||
goto err_disable_aclk;
|
||||
}
|
||||
|
@ -1434,7 +1435,7 @@ static int vop_initial(struct vop *vop)
|
|||
*/
|
||||
vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk");
|
||||
if (IS_ERR(vop->dclk_rst)) {
|
||||
dev_err(vop->dev, "failed to get dclk reset\n");
|
||||
DRM_DEV_ERROR(vop->dev, "failed to get dclk reset\n");
|
||||
ret = PTR_ERR(vop->dclk_rst);
|
||||
goto err_disable_aclk;
|
||||
}
|
||||
|
@ -1511,7 +1512,7 @@ int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout)
|
|||
vop_line_flag_irq_disable(vop);
|
||||
|
||||
if (jiffies_left == 0) {
|
||||
dev_err(vop->dev, "Timeout waiting for IRQ\n");
|
||||
DRM_DEV_ERROR(vop->dev, "Timeout waiting for IRQ\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
|
@ -1558,7 +1559,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
|
|||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "cannot find irq for vop\n");
|
||||
DRM_DEV_ERROR(dev, "cannot find irq for vop\n");
|
||||
return irq;
|
||||
}
|
||||
vop->irq = (unsigned int)irq;
|
||||
|
@ -1584,7 +1585,8 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
|
|||
|
||||
ret = vop_initial(vop);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
|
||||
DRM_DEV_ERROR(&pdev->dev,
|
||||
"cannot initial vop dev - err %d\n", ret);
|
||||
goto err_disable_pm_runtime;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||
* Author:
|
||||
* Mark Yao <mark.yao@rock-chips.com>
|
||||
* Sandy Huang <hjc@rock-chips.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_of.h>
|
||||
|
||||
#include <linux/component.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "rockchip_drm_drv.h"
|
||||
#include "rockchip_drm_vop.h"
|
||||
#include "rockchip_lvds.h"
|
||||
|
||||
#define DISPLAY_OUTPUT_RGB 0
|
||||
#define DISPLAY_OUTPUT_LVDS 1
|
||||
#define DISPLAY_OUTPUT_DUAL_LVDS 2
|
||||
|
||||
#define connector_to_lvds(c) \
|
||||
container_of(c, struct rockchip_lvds, connector)
|
||||
|
||||
#define encoder_to_lvds(c) \
|
||||
container_of(c, struct rockchip_lvds, encoder)
|
||||
|
||||
/**
|
||||
* rockchip_lvds_soc_data - rockchip lvds Soc private data
|
||||
* @ch1_offset: lvds channel 1 registe offset
|
||||
* grf_soc_con6: general registe offset for LVDS contrl
|
||||
* grf_soc_con7: general registe offset for LVDS contrl
|
||||
* has_vop_sel: to indicate whether need to choose from different VOP.
|
||||
*/
|
||||
struct rockchip_lvds_soc_data {
|
||||
u32 ch1_offset;
|
||||
int grf_soc_con6;
|
||||
int grf_soc_con7;
|
||||
bool has_vop_sel;
|
||||
};
|
||||
|
||||
struct rockchip_lvds {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct regmap *grf;
|
||||
struct clk *pclk;
|
||||
const struct rockchip_lvds_soc_data *soc_data;
|
||||
int output; /* rgb lvds or dual lvds output */
|
||||
int format; /* vesa or jeida format */
|
||||
struct drm_device *drm_dev;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
struct dev_pin_info *pins;
|
||||
};
|
||||
|
||||
static inline void lvds_writel(struct rockchip_lvds *lvds, u32 offset, u32 val)
|
||||
{
|
||||
writel_relaxed(val, lvds->regs + offset);
|
||||
if (lvds->output == DISPLAY_OUTPUT_LVDS)
|
||||
return;
|
||||
writel_relaxed(val, lvds->regs + offset + lvds->soc_data->ch1_offset);
|
||||
}
|
||||
|
||||
static inline int lvds_name_to_format(const char *s)
|
||||
{
|
||||
if (strncmp(s, "jeida-18", 8) == 0)
|
||||
return LVDS_JEIDA_18;
|
||||
else if (strncmp(s, "jeida-24", 8) == 0)
|
||||
return LVDS_JEIDA_24;
|
||||
else if (strncmp(s, "vesa-24", 7) == 0)
|
||||
return LVDS_VESA_24;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int lvds_name_to_output(const char *s)
|
||||
{
|
||||
if (strncmp(s, "rgb", 3) == 0)
|
||||
return DISPLAY_OUTPUT_RGB;
|
||||
else if (strncmp(s, "lvds", 4) == 0)
|
||||
return DISPLAY_OUTPUT_LVDS;
|
||||
else if (strncmp(s, "duallvds", 8) == 0)
|
||||
return DISPLAY_OUTPUT_DUAL_LVDS;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int rockchip_lvds_poweron(struct rockchip_lvds *lvds)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = clk_enable(lvds->pclk);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(lvds->dev, "failed to enable lvds pclk %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = pm_runtime_get_sync(lvds->dev);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret);
|
||||
clk_disable(lvds->pclk);
|
||||
return ret;
|
||||
}
|
||||
val = RK3288_LVDS_CH0_REG0_LANE4_EN | RK3288_LVDS_CH0_REG0_LANE3_EN |
|
||||
RK3288_LVDS_CH0_REG0_LANE2_EN | RK3288_LVDS_CH0_REG0_LANE1_EN |
|
||||
RK3288_LVDS_CH0_REG0_LANE0_EN;
|
||||
if (lvds->output == DISPLAY_OUTPUT_RGB) {
|
||||
val |= RK3288_LVDS_CH0_REG0_TTL_EN |
|
||||
RK3288_LVDS_CH0_REG0_LANECK_EN;
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG0, val);
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
|
||||
RK3288_LVDS_PLL_FBDIV_REG2(0x46));
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG4,
|
||||
RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
|
||||
RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
|
||||
RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
|
||||
RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
|
||||
RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
|
||||
RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG5,
|
||||
RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
|
||||
RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
|
||||
RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
|
||||
RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
|
||||
RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
|
||||
RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
|
||||
} else {
|
||||
val |= RK3288_LVDS_CH0_REG0_LVDS_EN |
|
||||
RK3288_LVDS_CH0_REG0_LANECK_EN;
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG0, val);
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG1,
|
||||
RK3288_LVDS_CH0_REG1_LANECK_BIAS |
|
||||
RK3288_LVDS_CH0_REG1_LANE4_BIAS |
|
||||
RK3288_LVDS_CH0_REG1_LANE3_BIAS |
|
||||
RK3288_LVDS_CH0_REG1_LANE2_BIAS |
|
||||
RK3288_LVDS_CH0_REG1_LANE1_BIAS |
|
||||
RK3288_LVDS_CH0_REG1_LANE0_BIAS);
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
|
||||
RK3288_LVDS_CH0_REG2_RESERVE_ON |
|
||||
RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
|
||||
RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
|
||||
RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
|
||||
RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
|
||||
RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
|
||||
RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
|
||||
RK3288_LVDS_PLL_FBDIV_REG2(0x46));
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00);
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00);
|
||||
}
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG3, RK3288_LVDS_PLL_FBDIV_REG3(0x46));
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REGD, RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
|
||||
lvds_writel(lvds, RK3288_LVDS_CH0_REG20, RK3288_LVDS_CH0_REG20_LSB);
|
||||
|
||||
lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE);
|
||||
lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE);
|
||||
lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE);
|
||||
val = LVDS_DUAL | LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN | LVDS_PWRDN;
|
||||
val |= val << 16;
|
||||
ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
|
||||
if (ret != 0)
|
||||
DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret);
|
||||
|
||||
pm_runtime_put(lvds->dev);
|
||||
clk_disable(lvds->pclk);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs rockchip_lvds_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 rockchip_lvds_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct rockchip_lvds *lvds = connector_to_lvds(connector);
|
||||
struct drm_panel *panel = lvds->panel;
|
||||
|
||||
return drm_panel_get_modes(panel);
|
||||
}
|
||||
|
||||
static const
|
||||
struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = {
|
||||
.get_modes = rockchip_lvds_connector_get_modes,
|
||||
};
|
||||
|
||||
static void rockchip_lvds_grf_config(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
|
||||
u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
|
||||
u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* iomux to LCD data/sync mode */
|
||||
if (lvds->output == DISPLAY_OUTPUT_RGB)
|
||||
if (lvds->pins && !IS_ERR(lvds->pins->default_state))
|
||||
pinctrl_select_state(lvds->pins->p,
|
||||
lvds->pins->default_state);
|
||||
val = lvds->format | LVDS_CH0_EN;
|
||||
if (lvds->output == DISPLAY_OUTPUT_RGB)
|
||||
val |= LVDS_TTL_EN | LVDS_CH1_EN;
|
||||
else if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS)
|
||||
val |= LVDS_DUAL | LVDS_CH1_EN;
|
||||
|
||||
if ((mode->htotal - mode->hsync_start) & 0x01)
|
||||
val |= LVDS_START_PHASE_RST_1;
|
||||
|
||||
val |= (pin_dclk << 8) | (pin_hsync << 9);
|
||||
val |= (0xffff << 16);
|
||||
ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
|
||||
if (ret != 0) {
|
||||
DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int rockchip_lvds_set_vop_source(struct rockchip_lvds *lvds,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!lvds->soc_data->has_vop_sel)
|
||||
return 0;
|
||||
|
||||
ret = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
|
||||
if (ret)
|
||||
val |= RK3288_LVDS_SOC_CON6_SEL_VOP_LIT;
|
||||
|
||||
ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
|
||||
|
||||
s->output_mode = ROCKCHIP_OUT_MODE_P888;
|
||||
s->output_type = DRM_MODE_CONNECTOR_LVDS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rockchip_lvds_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
|
||||
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
|
||||
int ret;
|
||||
|
||||
drm_panel_prepare(lvds->panel);
|
||||
ret = rockchip_lvds_poweron(lvds);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(lvds->dev, "failed to power on lvds: %d\n", ret);
|
||||
drm_panel_unprepare(lvds->panel);
|
||||
}
|
||||
rockchip_lvds_grf_config(encoder, mode);
|
||||
rockchip_lvds_set_vop_source(lvds, encoder);
|
||||
drm_panel_enable(lvds->panel);
|
||||
}
|
||||
|
||||
static void rockchip_lvds_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
|
||||
|
||||
drm_panel_disable(lvds->panel);
|
||||
rockchip_lvds_poweroff(lvds);
|
||||
drm_panel_unprepare(lvds->panel);
|
||||
}
|
||||
|
||||
static const
|
||||
struct drm_encoder_helper_funcs rockchip_lvds_encoder_helper_funcs = {
|
||||
.enable = rockchip_lvds_encoder_enable,
|
||||
.disable = rockchip_lvds_encoder_disable,
|
||||
.atomic_check = rockchip_lvds_encoder_atomic_check,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
static const struct rockchip_lvds_soc_data rk3288_lvds_data = {
|
||||
.ch1_offset = 0x100,
|
||||
.grf_soc_con6 = 0x025c,
|
||||
.grf_soc_con7 = 0x0260,
|
||||
.has_vop_sel = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_lvds_dt_ids[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3288-lvds",
|
||||
.data = &rk3288_lvds_data
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
|
||||
|
||||
static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct rockchip_lvds *lvds = dev_get_drvdata(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_connector *connector;
|
||||
struct device_node *remote = NULL;
|
||||
struct device_node *port, *endpoint;
|
||||
int ret;
|
||||
const char *name;
|
||||
u32 endpoint_id;
|
||||
|
||||
lvds->drm_dev = drm_dev;
|
||||
port = of_graph_get_port_by_id(dev->of_node, 1);
|
||||
if (!port) {
|
||||
DRM_DEV_ERROR(dev,
|
||||
"can't found port point, please init lvds panel port!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for_each_child_of_node(port, endpoint) {
|
||||
of_property_read_u32(endpoint, "reg", &endpoint_id);
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, endpoint_id,
|
||||
&lvds->panel, &lvds->bridge);
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to find panel and bridge node\n");
|
||||
ret = -EPROBE_DEFER;
|
||||
goto err_put_port;
|
||||
}
|
||||
if (lvds->panel)
|
||||
remote = lvds->panel->dev->of_node;
|
||||
else
|
||||
remote = lvds->bridge->of_node;
|
||||
if (of_property_read_string(dev->of_node, "rockchip,output", &name))
|
||||
/* default set it as output rgb */
|
||||
lvds->output = DISPLAY_OUTPUT_RGB;
|
||||
else
|
||||
lvds->output = lvds_name_to_output(name);
|
||||
|
||||
if (lvds->output < 0) {
|
||||
DRM_DEV_ERROR(dev, "invalid output type [%s]\n", name);
|
||||
ret = lvds->output;
|
||||
goto err_put_remote;
|
||||
}
|
||||
|
||||
if (of_property_read_string(remote, "data-mapping", &name))
|
||||
/* default set it as format vesa 18 */
|
||||
lvds->format = LVDS_VESA_18;
|
||||
else
|
||||
lvds->format = lvds_name_to_format(name);
|
||||
|
||||
if (lvds->format < 0) {
|
||||
DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n", name);
|
||||
ret = lvds->format;
|
||||
goto err_put_remote;
|
||||
}
|
||||
|
||||
encoder = &lvds->encoder;
|
||||
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
|
||||
dev->of_node);
|
||||
|
||||
ret = drm_encoder_init(drm_dev, encoder, &rockchip_lvds_encoder_funcs,
|
||||
DRM_MODE_ENCODER_LVDS, NULL);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(drm_dev->dev,
|
||||
"failed to initialize encoder: %d\n", ret);
|
||||
goto err_put_remote;
|
||||
}
|
||||
|
||||
drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs);
|
||||
|
||||
if (lvds->panel) {
|
||||
connector = &lvds->connector;
|
||||
connector->dpms = DRM_MODE_DPMS_OFF;
|
||||
ret = drm_connector_init(drm_dev, connector,
|
||||
&rockchip_lvds_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(drm_dev->dev,
|
||||
"failed to initialize connector: %d\n", ret);
|
||||
goto err_free_encoder;
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector,
|
||||
&rockchip_lvds_connector_helper_funcs);
|
||||
|
||||
ret = drm_mode_connector_attach_encoder(connector, encoder);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(drm_dev->dev,
|
||||
"failed to attach encoder: %d\n", ret);
|
||||
goto err_free_connector;
|
||||
}
|
||||
|
||||
ret = drm_panel_attach(lvds->panel, connector);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(drm_dev->dev,
|
||||
"failed to attach panel: %d\n", ret);
|
||||
goto err_free_connector;
|
||||
}
|
||||
} else {
|
||||
lvds->bridge->encoder = encoder;
|
||||
ret = drm_bridge_attach(encoder, lvds->bridge, NULL);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(drm_dev->dev,
|
||||
"failed to attach bridge: %d\n", ret);
|
||||
goto err_free_encoder;
|
||||
}
|
||||
encoder->bridge = lvds->bridge;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
of_node_put(remote);
|
||||
of_node_put(port);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_connector:
|
||||
drm_connector_cleanup(connector);
|
||||
err_free_encoder:
|
||||
drm_encoder_cleanup(encoder);
|
||||
err_put_remote:
|
||||
of_node_put(remote);
|
||||
err_put_port:
|
||||
of_node_put(port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rockchip_lvds_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct rockchip_lvds *lvds = dev_get_drvdata(dev);
|
||||
|
||||
rockchip_lvds_encoder_disable(&lvds->encoder);
|
||||
if (lvds->panel)
|
||||
drm_panel_detach(lvds->panel);
|
||||
pm_runtime_disable(dev);
|
||||
drm_connector_cleanup(&lvds->connector);
|
||||
drm_encoder_cleanup(&lvds->encoder);
|
||||
}
|
||||
|
||||
static const struct component_ops rockchip_lvds_component_ops = {
|
||||
.bind = rockchip_lvds_bind,
|
||||
.unbind = rockchip_lvds_unbind,
|
||||
};
|
||||
|
||||
static int rockchip_lvds_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rockchip_lvds *lvds;
|
||||
const struct of_device_id *match;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
|
||||
if (!lvds)
|
||||
return -ENOMEM;
|
||||
|
||||
lvds->dev = dev;
|
||||
match = of_match_node(rockchip_lvds_dt_ids, dev->of_node);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
lvds->soc_data = match->data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
lvds->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(lvds->regs))
|
||||
return PTR_ERR(lvds->regs);
|
||||
|
||||
lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds");
|
||||
if (IS_ERR(lvds->pclk)) {
|
||||
DRM_DEV_ERROR(dev, "could not get pclk_lvds\n");
|
||||
return PTR_ERR(lvds->pclk);
|
||||
}
|
||||
|
||||
lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins),
|
||||
GFP_KERNEL);
|
||||
if (!lvds->pins)
|
||||
return -ENOMEM;
|
||||
|
||||
lvds->pins->p = devm_pinctrl_get(lvds->dev);
|
||||
if (IS_ERR(lvds->pins->p)) {
|
||||
DRM_DEV_ERROR(dev, "no pinctrl handle\n");
|
||||
devm_kfree(lvds->dev, lvds->pins);
|
||||
lvds->pins = NULL;
|
||||
} else {
|
||||
lvds->pins->default_state =
|
||||
pinctrl_lookup_state(lvds->pins->p, "lcdc");
|
||||
if (IS_ERR(lvds->pins->default_state)) {
|
||||
DRM_DEV_ERROR(dev, "no default pinctrl state\n");
|
||||
devm_kfree(lvds->dev, lvds->pins);
|
||||
lvds->pins = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"rockchip,grf");
|
||||
if (IS_ERR(lvds->grf)) {
|
||||
DRM_DEV_ERROR(dev, "missing rockchip,grf property\n");
|
||||
return PTR_ERR(lvds->grf);
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, lvds);
|
||||
|
||||
ret = clk_prepare(lvds->pclk);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(dev, "failed to prepare pclk_lvds\n");
|
||||
return ret;
|
||||
}
|
||||
ret = component_add(&pdev->dev, &rockchip_lvds_component_ops);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(dev, "failed to add component\n");
|
||||
clk_unprepare(lvds->pclk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_lvds_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
component_del(&pdev->dev, &rockchip_lvds_component_ops);
|
||||
clk_unprepare(lvds->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_driver rockchip_lvds_driver = {
|
||||
.probe = rockchip_lvds_probe,
|
||||
.remove = rockchip_lvds_remove,
|
||||
.driver = {
|
||||
.name = "rockchip-lvds",
|
||||
.of_match_table = of_match_ptr(rockchip_lvds_dt_ids),
|
||||
},
|
||||
};
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||
* Author:
|
||||
* Sandy Huang <hjc@rock-chips.com>
|
||||
* Mark Yao <mark.yao@rock-chips.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _ROCKCHIP_LVDS_
|
||||
#define _ROCKCHIP_LVDS_
|
||||
|
||||
#define RK3288_LVDS_CH0_REG0 0x00
|
||||
#define RK3288_LVDS_CH0_REG0_LVDS_EN BIT(7)
|
||||
#define RK3288_LVDS_CH0_REG0_TTL_EN BIT(6)
|
||||
#define RK3288_LVDS_CH0_REG0_LANECK_EN BIT(5)
|
||||
#define RK3288_LVDS_CH0_REG0_LANE4_EN BIT(4)
|
||||
#define RK3288_LVDS_CH0_REG0_LANE3_EN BIT(3)
|
||||
#define RK3288_LVDS_CH0_REG0_LANE2_EN BIT(2)
|
||||
#define RK3288_LVDS_CH0_REG0_LANE1_EN BIT(1)
|
||||
#define RK3288_LVDS_CH0_REG0_LANE0_EN BIT(0)
|
||||
|
||||
#define RK3288_LVDS_CH0_REG1 0x04
|
||||
#define RK3288_LVDS_CH0_REG1_LANECK_BIAS BIT(5)
|
||||
#define RK3288_LVDS_CH0_REG1_LANE4_BIAS BIT(4)
|
||||
#define RK3288_LVDS_CH0_REG1_LANE3_BIAS BIT(3)
|
||||
#define RK3288_LVDS_CH0_REG1_LANE2_BIAS BIT(2)
|
||||
#define RK3288_LVDS_CH0_REG1_LANE1_BIAS BIT(1)
|
||||
#define RK3288_LVDS_CH0_REG1_LANE0_BIAS BIT(0)
|
||||
|
||||
#define RK3288_LVDS_CH0_REG2 0x08
|
||||
#define RK3288_LVDS_CH0_REG2_RESERVE_ON BIT(7)
|
||||
#define RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE BIT(6)
|
||||
#define RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE BIT(5)
|
||||
#define RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE BIT(4)
|
||||
#define RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE BIT(3)
|
||||
#define RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE BIT(2)
|
||||
#define RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE BIT(1)
|
||||
#define RK3288_LVDS_CH0_REG2_PLL_FBDIV8 BIT(0)
|
||||
|
||||
#define RK3288_LVDS_CH0_REG3 0x0c
|
||||
#define RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK 0xff
|
||||
|
||||
#define RK3288_LVDS_CH0_REG4 0x10
|
||||
#define RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE BIT(5)
|
||||
#define RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE BIT(4)
|
||||
#define RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE BIT(3)
|
||||
#define RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE BIT(2)
|
||||
#define RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE BIT(1)
|
||||
#define RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE BIT(0)
|
||||
|
||||
#define RK3288_LVDS_CH0_REG5 0x14
|
||||
#define RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA BIT(5)
|
||||
#define RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA BIT(4)
|
||||
#define RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA BIT(3)
|
||||
#define RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA BIT(2)
|
||||
#define RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA BIT(1)
|
||||
#define RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA BIT(0)
|
||||
|
||||
#define RK3288_LVDS_CFG_REGC 0x30
|
||||
#define RK3288_LVDS_CFG_REGC_PLL_ENABLE 0x00
|
||||
#define RK3288_LVDS_CFG_REGC_PLL_DISABLE 0xff
|
||||
|
||||
#define RK3288_LVDS_CH0_REGD 0x34
|
||||
#define RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK 0x1f
|
||||
|
||||
#define RK3288_LVDS_CH0_REG20 0x80
|
||||
#define RK3288_LVDS_CH0_REG20_MSB 0x45
|
||||
#define RK3288_LVDS_CH0_REG20_LSB 0x44
|
||||
|
||||
#define RK3288_LVDS_CFG_REG21 0x84
|
||||
#define RK3288_LVDS_CFG_REG21_TX_ENABLE 0x92
|
||||
#define RK3288_LVDS_CFG_REG21_TX_DISABLE 0x00
|
||||
#define RK3288_LVDS_CH1_OFFSET 0x100
|
||||
|
||||
/* fbdiv value is split over 2 registers, with bit8 in reg2 */
|
||||
#define RK3288_LVDS_PLL_FBDIV_REG2(_fbd) \
|
||||
(_fbd & BIT(8) ? RK3288_LVDS_CH0_REG2_PLL_FBDIV8 : 0)
|
||||
#define RK3288_LVDS_PLL_FBDIV_REG3(_fbd) \
|
||||
(_fbd & RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK)
|
||||
#define RK3288_LVDS_PLL_PREDIV_REGD(_pd) \
|
||||
(_pd & RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK)
|
||||
|
||||
#define RK3288_LVDS_SOC_CON6_SEL_VOP_LIT BIT(3)
|
||||
|
||||
#define LVDS_FMT_MASK (0x07 << 16)
|
||||
#define LVDS_MSB BIT(3)
|
||||
#define LVDS_DUAL BIT(4)
|
||||
#define LVDS_FMT_1 BIT(5)
|
||||
#define LVDS_TTL_EN BIT(6)
|
||||
#define LVDS_START_PHASE_RST_1 BIT(7)
|
||||
#define LVDS_DCLK_INV BIT(8)
|
||||
#define LVDS_CH0_EN BIT(11)
|
||||
#define LVDS_CH1_EN BIT(12)
|
||||
#define LVDS_PWRDN BIT(15)
|
||||
|
||||
#define LVDS_24BIT (0 << 1)
|
||||
#define LVDS_18BIT (1 << 1)
|
||||
#define LVDS_FORMAT_VESA (0 << 0)
|
||||
#define LVDS_FORMAT_JEIDA (1 << 0)
|
||||
|
||||
#define LVDS_VESA_24 0
|
||||
#define LVDS_JEIDA_24 1
|
||||
#define LVDS_VESA_18 2
|
||||
#define LVDS_JEIDA_18 3
|
||||
|
||||
#endif /* _ROCKCHIP_LVDS_ */
|
|
@ -533,7 +533,7 @@ static int vop_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
|
||||
if (!dev->of_node) {
|
||||
dev_err(dev, "can't find vop devices\n");
|
||||
DRM_DEV_ERROR(dev, "can't find vop devices\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
|
||||
|
@ -145,7 +146,7 @@ static void sti_output_poll_changed(struct drm_device *ddev)
|
|||
}
|
||||
|
||||
static const struct drm_mode_config_funcs sti_mode_config_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.fb_create = drm_gem_fb_create,
|
||||
.output_poll_changed = sti_output_poll_changed,
|
||||
.atomic_check = sti_atomic_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
|
|
|
@ -463,11 +463,7 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
|
|||
bridge->driver_private = dvo;
|
||||
bridge->funcs = &sti_dvo_bridge_funcs;
|
||||
bridge->of_node = dvo->dev.of_node;
|
||||
err = drm_bridge_add(bridge);
|
||||
if (err) {
|
||||
DRM_ERROR("Failed to add bridge\n");
|
||||
return err;
|
||||
}
|
||||
drm_bridge_add(bridge);
|
||||
|
||||
err = drm_bridge_attach(encoder, bridge, NULL);
|
||||
if (err) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
|
||||
#include "ltdc.h"
|
||||
|
||||
|
@ -31,7 +32,7 @@ static void drv_output_poll_changed(struct drm_device *ddev)
|
|||
}
|
||||
|
||||
static const struct drm_mode_config_funcs drv_mode_config_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.fb_create = drm_gem_fb_create,
|
||||
.output_poll_changed = drv_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
|
|
|
@ -113,11 +113,13 @@ static enum dsi_color dsi_color_from_mipi(enum mipi_dsi_pixel_format fmt)
|
|||
|
||||
static int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf)
|
||||
{
|
||||
/* prevent from division by 0 */
|
||||
if (idf * odf)
|
||||
return DIV_ROUND_CLOSEST(clkin_khz * ndiv, idf * odf);
|
||||
int divisor = idf * odf;
|
||||
|
||||
return 0;
|
||||
/* prevent from division by 0 */
|
||||
if (!divisor)
|
||||
return 0;
|
||||
|
||||
return DIV_ROUND_CLOSEST(clkin_khz * ndiv, divisor);
|
||||
}
|
||||
|
||||
static int dsi_pll_get_params(int clkin_khz, int clkout_khz,
|
||||
|
|
|
@ -106,11 +106,6 @@ static int sun4i_drv_bind(struct device *dev)
|
|||
goto free_drm;
|
||||
}
|
||||
|
||||
/* drm_vblank_init calls kcalloc, which can fail */
|
||||
ret = drm_vblank_init(drm, 1);
|
||||
if (ret)
|
||||
goto free_mem_region;
|
||||
|
||||
drm_mode_config_init(drm);
|
||||
|
||||
ret = component_bind_all(drm->dev, drm);
|
||||
|
@ -119,6 +114,11 @@ static int sun4i_drv_bind(struct device *dev)
|
|||
goto cleanup_mode_config;
|
||||
}
|
||||
|
||||
/* drm_vblank_init calls kcalloc, which can fail */
|
||||
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
|
||||
if (ret)
|
||||
goto free_mem_region;
|
||||
|
||||
drm->irq_enabled = true;
|
||||
|
||||
/* Remove early framebuffers (ie. simplefb) */
|
||||
|
@ -200,11 +200,39 @@ static int compare_of(struct device *dev, void *data)
|
|||
return dev->of_node == data;
|
||||
}
|
||||
|
||||
/*
|
||||
* The encoder drivers use drm_of_find_possible_crtcs to get upstream
|
||||
* crtcs from the device tree using of_graph. For the results to be
|
||||
* correct, encoders must be probed/bound after _all_ crtcs have been
|
||||
* created. The existing code uses a depth first recursive traversal
|
||||
* of the of_graph, which means the encoders downstream of the TCON
|
||||
* get add right after the first TCON. The second TCON or CRTC will
|
||||
* never be properly associated with encoders connected to it.
|
||||
*
|
||||
* Also, in a dual display pipeline setup, both frontends can feed
|
||||
* either backend, and both backends can feed either TCON, we want
|
||||
* all components of the same type to be added before the next type
|
||||
* in the pipeline. Fortunately, the pipelines are perfectly symmetric,
|
||||
* i.e. components of the same type are at the same depth when counted
|
||||
* from the frontend. The only exception is the third pipeline in
|
||||
* the A80 SoC, which we do not support anyway.
|
||||
*
|
||||
* Hence we can use a breadth first search traversal order to add
|
||||
* components. We do not need to check for duplicates. The component
|
||||
* matching system handles this for us.
|
||||
*/
|
||||
struct endpoint_list {
|
||||
struct device_node *node;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static int sun4i_drv_add_endpoints(struct device *dev,
|
||||
struct list_head *endpoints,
|
||||
struct component_match **match,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct device_node *port, *ep, *remote;
|
||||
struct endpoint_list *endpoint;
|
||||
int count = 0;
|
||||
|
||||
/*
|
||||
|
@ -264,10 +292,15 @@ static int sun4i_drv_add_endpoints(struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
/* Walk down our tree */
|
||||
count += sun4i_drv_add_endpoints(dev, match, remote);
|
||||
/* Add downstream nodes to the queue */
|
||||
endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL);
|
||||
if (!endpoint) {
|
||||
of_node_put(remote);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
of_node_put(remote);
|
||||
endpoint->node = remote;
|
||||
list_add_tail(&endpoint->list, endpoints);
|
||||
}
|
||||
|
||||
return count;
|
||||
|
@ -277,7 +310,9 @@ static int sun4i_drv_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct component_match *match = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int i, count = 0;
|
||||
struct endpoint_list *endpoint, *endpoint_temp;
|
||||
int i, ret, count = 0;
|
||||
LIST_HEAD(endpoints);
|
||||
|
||||
for (i = 0;; i++) {
|
||||
struct device_node *pipeline = of_parse_phandle(np,
|
||||
|
@ -286,12 +321,31 @@ static int sun4i_drv_probe(struct platform_device *pdev)
|
|||
if (!pipeline)
|
||||
break;
|
||||
|
||||
count += sun4i_drv_add_endpoints(&pdev->dev, &match,
|
||||
pipeline);
|
||||
of_node_put(pipeline);
|
||||
endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL);
|
||||
if (!endpoint) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_endpoints;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("Queued %d outputs on pipeline %d\n",
|
||||
count, i);
|
||||
endpoint->node = pipeline;
|
||||
list_add_tail(&endpoint->list, &endpoints);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(endpoint, endpoint_temp, &endpoints, list) {
|
||||
/* process this endpoint */
|
||||
ret = sun4i_drv_add_endpoints(&pdev->dev, &endpoints, &match,
|
||||
endpoint->node);
|
||||
|
||||
/* sun4i_drv_add_endpoints can fail to allocate memory */
|
||||
if (ret < 0)
|
||||
goto err_free_endpoints;
|
||||
|
||||
count += ret;
|
||||
|
||||
/* delete and cleanup the current entry */
|
||||
list_del(&endpoint->list);
|
||||
of_node_put(endpoint->node);
|
||||
kfree(endpoint);
|
||||
}
|
||||
|
||||
if (count)
|
||||
|
@ -300,6 +354,15 @@ static int sun4i_drv_probe(struct platform_device *pdev)
|
|||
match);
|
||||
else
|
||||
return 0;
|
||||
|
||||
err_free_endpoints:
|
||||
list_for_each_entry_safe(endpoint, endpoint_temp, &endpoints, list) {
|
||||
list_del(&endpoint->list);
|
||||
of_node_put(endpoint->node);
|
||||
kfree(endpoint);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sun4i_drv_remove(struct platform_device *pdev)
|
||||
|
|
|
@ -463,40 +463,168 @@ static int sun4i_tcon_init_regmap(struct device *dev,
|
|||
* function in fact searches the corresponding engine, and the ID is
|
||||
* requested via the get_id function of the engine.
|
||||
*/
|
||||
static struct sunxi_engine *
|
||||
sun4i_tcon_find_engine_traverse(struct sun4i_drv *drv,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct device_node *port, *ep, *remote;
|
||||
struct sunxi_engine *engine = ERR_PTR(-EINVAL);
|
||||
|
||||
port = of_graph_get_port_by_id(node, 0);
|
||||
if (!port)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/*
|
||||
* This only works if there is only one path from the TCON
|
||||
* to any display engine. Otherwise the probe order of the
|
||||
* TCONs and display engines is not guaranteed. They may
|
||||
* either bind to the wrong one, or worse, bind to the same
|
||||
* one if additional checks are not done.
|
||||
*
|
||||
* Bail out if there are multiple input connections.
|
||||
*/
|
||||
if (of_get_available_child_count(port) != 1)
|
||||
goto out_put_port;
|
||||
|
||||
/* Get the first connection without specifying an ID */
|
||||
ep = of_get_next_available_child(port, NULL);
|
||||
if (!ep)
|
||||
goto out_put_port;
|
||||
|
||||
remote = of_graph_get_remote_port_parent(ep);
|
||||
if (!remote)
|
||||
goto out_put_ep;
|
||||
|
||||
/* does this node match any registered engines? */
|
||||
list_for_each_entry(engine, &drv->engine_list, list)
|
||||
if (remote == engine->node)
|
||||
goto out_put_remote;
|
||||
|
||||
/* keep looking through upstream ports */
|
||||
engine = sun4i_tcon_find_engine_traverse(drv, remote);
|
||||
|
||||
out_put_remote:
|
||||
of_node_put(remote);
|
||||
out_put_ep:
|
||||
of_node_put(ep);
|
||||
out_put_port:
|
||||
of_node_put(port);
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
||||
/*
|
||||
* The device tree binding says that the remote endpoint ID of any
|
||||
* connection between components, up to and including the TCON, of
|
||||
* the display pipeline should be equal to the actual ID of the local
|
||||
* component. Thus we can look at any one of the input connections of
|
||||
* the TCONs, and use that connection's remote endpoint ID as our own.
|
||||
*
|
||||
* Since the user of this function already finds the input port,
|
||||
* the port is passed in directly without further checks.
|
||||
*/
|
||||
static int sun4i_tcon_of_get_id_from_port(struct device_node *port)
|
||||
{
|
||||
struct device_node *ep;
|
||||
int ret = -EINVAL;
|
||||
|
||||
/* try finding an upstream endpoint */
|
||||
for_each_available_child_of_node(port, ep) {
|
||||
struct device_node *remote;
|
||||
u32 reg;
|
||||
|
||||
remote = of_graph_get_remote_endpoint(ep);
|
||||
if (!remote)
|
||||
continue;
|
||||
|
||||
ret = of_property_read_u32(remote, "reg", ®);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
ret = reg;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Once we know the TCON's id, we can look through the list of
|
||||
* engines to find a matching one. We assume all engines have
|
||||
* been probed and added to the list.
|
||||
*/
|
||||
static struct sunxi_engine *sun4i_tcon_get_engine_by_id(struct sun4i_drv *drv,
|
||||
int id)
|
||||
{
|
||||
struct sunxi_engine *engine;
|
||||
|
||||
list_for_each_entry(engine, &drv->engine_list, list)
|
||||
if (engine->id == id)
|
||||
return engine;
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* On SoCs with the old display pipeline design (Display Engine 1.0),
|
||||
* we assumed the TCON was always tied to just one backend. However
|
||||
* this proved not to be the case. On the A31, the TCON can select
|
||||
* either backend as its source. On the A20 (and likely on the A10),
|
||||
* the backend can choose which TCON to output to.
|
||||
*
|
||||
* The device tree binding says that the remote endpoint ID of any
|
||||
* connection between components, up to and including the TCON, of
|
||||
* the display pipeline should be equal to the actual ID of the local
|
||||
* component. Thus we should be able to look at any one of the input
|
||||
* connections of the TCONs, and use that connection's remote endpoint
|
||||
* ID as our own.
|
||||
*
|
||||
* However the connections between the backend and TCON were assumed
|
||||
* to be always singular, and their endpoit IDs were all incorrectly
|
||||
* set to 0. This means for these old device trees, we cannot just look
|
||||
* up the remote endpoint ID of a TCON input endpoint. TCON1 would be
|
||||
* incorrectly identified as TCON0.
|
||||
*
|
||||
* This function first checks if the TCON node has 2 input endpoints.
|
||||
* If so, then the device tree is a corrected version, and it will use
|
||||
* sun4i_tcon_of_get_id() and sun4i_tcon_get_engine_by_id() from above
|
||||
* to fetch the ID and engine directly. If not, then it is likely an
|
||||
* old device trees, where the endpoint IDs were incorrect, but did not
|
||||
* have endpoint connections between the backend and TCON across
|
||||
* different display pipelines. It will fall back to the old method of
|
||||
* traversing the of_graph to try and find a matching engine by device
|
||||
* node.
|
||||
*
|
||||
* In the case of single display pipeline device trees, either method
|
||||
* works.
|
||||
*/
|
||||
static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct device_node *port, *ep, *remote;
|
||||
struct device_node *port;
|
||||
struct sunxi_engine *engine;
|
||||
|
||||
port = of_graph_get_port_by_id(node, 0);
|
||||
if (!port)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
for_each_available_child_of_node(port, ep) {
|
||||
remote = of_graph_get_remote_port_parent(ep);
|
||||
if (!remote)
|
||||
continue;
|
||||
/*
|
||||
* Is this a corrected device tree with cross pipeline
|
||||
* connections between the backend and TCON?
|
||||
*/
|
||||
if (of_get_child_count(port) > 1) {
|
||||
/* Get our ID directly from an upstream endpoint */
|
||||
int id = sun4i_tcon_of_get_id_from_port(port);
|
||||
|
||||
/* does this node match any registered engines? */
|
||||
list_for_each_entry(engine, &drv->engine_list, list) {
|
||||
if (remote == engine->node) {
|
||||
of_node_put(remote);
|
||||
of_node_put(port);
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
/* Get our engine by matching our ID */
|
||||
engine = sun4i_tcon_get_engine_by_id(drv, id);
|
||||
|
||||
/* keep looking through upstream ports */
|
||||
engine = sun4i_tcon_find_engine(drv, remote);
|
||||
if (!IS_ERR(engine)) {
|
||||
of_node_put(remote);
|
||||
of_node_put(port);
|
||||
return engine;
|
||||
}
|
||||
of_node_put(port);
|
||||
return engine;
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
/* Fallback to old method by traversing input endpoints */
|
||||
of_node_put(port);
|
||||
return sun4i_tcon_find_engine_traverse(drv, node);
|
||||
}
|
||||
|
||||
static int sun4i_tcon_bind(struct device *dev, struct device *master,
|
||||
|
@ -530,10 +658,7 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
|
|||
}
|
||||
|
||||
/* Make sure our TCON is reset */
|
||||
if (!reset_control_status(tcon->lcd_rst))
|
||||
reset_control_assert(tcon->lcd_rst);
|
||||
|
||||
ret = reset_control_deassert(tcon->lcd_rst);
|
||||
ret = reset_control_reset(tcon->lcd_rst);
|
||||
if (ret) {
|
||||
dev_err(dev, "Couldn't deassert our reset line\n");
|
||||
return ret;
|
||||
|
@ -574,6 +699,25 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
|
|||
if (ret < 0)
|
||||
goto err_free_clocks;
|
||||
|
||||
if (tcon->quirks->needs_de_be_mux) {
|
||||
/*
|
||||
* We assume there is no dynamic muxing of backends
|
||||
* and TCONs, so we select the backend with same ID.
|
||||
*
|
||||
* While dynamic selection might be interesting, since
|
||||
* the CRTC is tied to the TCON, while the layers are
|
||||
* tied to the backends, this means, we will need to
|
||||
* switch between groups of layers. There might not be
|
||||
* a way to represent this constraint in DRM.
|
||||
*/
|
||||
regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
|
||||
SUN4I_TCON0_CTL_SRC_SEL_MASK,
|
||||
tcon->id);
|
||||
regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
|
||||
SUN4I_TCON1_CTL_SRC_SEL_MASK,
|
||||
tcon->id);
|
||||
}
|
||||
|
||||
list_add_tail(&tcon->list, &drv->tcon_list);
|
||||
|
||||
return 0;
|
||||
|
@ -629,11 +773,13 @@ static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
|
|||
};
|
||||
|
||||
static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
|
||||
.has_channel_1 = true,
|
||||
.has_channel_1 = true,
|
||||
.needs_de_be_mux = true,
|
||||
};
|
||||
|
||||
static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {
|
||||
.has_channel_1 = true,
|
||||
.has_channel_1 = true,
|
||||
.needs_de_be_mux = true,
|
||||
};
|
||||
|
||||
static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#define SUN4I_TCON0_CTL_TCON_ENABLE BIT(31)
|
||||
#define SUN4I_TCON0_CTL_CLK_DELAY_MASK GENMASK(8, 4)
|
||||
#define SUN4I_TCON0_CTL_CLK_DELAY(delay) ((delay << 4) & SUN4I_TCON0_CTL_CLK_DELAY_MASK)
|
||||
#define SUN4I_TCON0_CTL_SRC_SEL_MASK GENMASK(2, 0)
|
||||
|
||||
#define SUN4I_TCON0_DCLK_REG 0x44
|
||||
#define SUN4I_TCON0_DCLK_GATE_BIT (31)
|
||||
|
@ -85,6 +86,7 @@
|
|||
#define SUN4I_TCON1_CTL_INTERLACE_ENABLE BIT(20)
|
||||
#define SUN4I_TCON1_CTL_CLK_DELAY_MASK GENMASK(8, 4)
|
||||
#define SUN4I_TCON1_CTL_CLK_DELAY(delay) ((delay << 4) & SUN4I_TCON1_CTL_CLK_DELAY_MASK)
|
||||
#define SUN4I_TCON1_CTL_SRC_SEL_MASK GENMASK(1, 0)
|
||||
|
||||
#define SUN4I_TCON1_BASIC0_REG 0x94
|
||||
#define SUN4I_TCON1_BASIC0_X(width) ((((width) - 1) & 0xfff) << 16)
|
||||
|
@ -146,6 +148,7 @@
|
|||
struct sun4i_tcon_quirks {
|
||||
bool has_unknown_mux; /* sun5i has undocumented mux */
|
||||
bool has_channel_1; /* a33 does not have channel 1 */
|
||||
bool needs_de_be_mux; /* sun6i needs mux to select backend */
|
||||
};
|
||||
|
||||
struct sun4i_tcon {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
|
||||
#include "tilcdc_drv.h"
|
||||
#include "tilcdc_regs.h"
|
||||
|
@ -65,7 +66,7 @@ static struct of_device_id tilcdc_of_match[];
|
|||
static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev,
|
||||
struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd)
|
||||
{
|
||||
return drm_fb_cma_create(dev, file_priv, mode_cmd);
|
||||
return drm_gem_fb_create(dev, file_priv, mode_cmd);
|
||||
}
|
||||
|
||||
static void tilcdc_fb_output_poll_changed(struct drm_device *dev)
|
||||
|
|
|
@ -163,7 +163,6 @@ MODULE_DEVICE_TABLE(spi, mi0283qt_id);
|
|||
static int mi0283qt_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
struct tinydrm_device *tdev;
|
||||
struct mipi_dbi *mipi;
|
||||
struct gpio_desc *dc;
|
||||
u32 rotation = 0;
|
||||
|
@ -215,20 +214,9 @@ static int mi0283qt_probe(struct spi_device *spi)
|
|||
return ret;
|
||||
}
|
||||
|
||||
tdev = &mipi->tinydrm;
|
||||
|
||||
ret = devm_tinydrm_register(tdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, mipi);
|
||||
|
||||
DRM_DEBUG_DRIVER("Initialized %s:%s @%uMHz on minor %d\n",
|
||||
tdev->drm->driver->name, dev_name(dev),
|
||||
spi->max_speed_hz / 1000000,
|
||||
tdev->drm->primary->index);
|
||||
|
||||
return 0;
|
||||
return devm_tinydrm_register(&mipi->tinydrm);
|
||||
}
|
||||
|
||||
static void mi0283qt_shutdown(struct spi_device *spi)
|
||||
|
|
|
@ -842,6 +842,8 @@ int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *mipi,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dbi_spi_init);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue