1
0
Fork 0

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
  ...
hifive-unleashed-5.1
Dave Airlie 2017-09-28 05:45:27 +10:00
commit 29baa82aa5
124 changed files with 4781 additions and 966 deletions

View File

@ -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

View File

@ -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>;
};
};
};

View File

@ -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>;
};
};

View File

@ -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>;
};
};

View File

@ -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 = <&reg_lcd_3v3>;
avdd-supply = <&reg_lcd_5v>;
port {
panel_in: endpoint {
remote-endpoint = <&display_out>;
};
};
};

View File

@ -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>;
};
};
};
};

View File

@ -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.

View File

@ -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:

View File

@ -15,6 +15,7 @@ Linux GPU Driver Developer's Guide
pl111
tegra
tinydrm
tve200
vc4
vga-switcheroo
vgaarbiter

View File

@ -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
=================

View File

@ -0,0 +1,6 @@
==================================
drm/tve200 Faraday TV Encoder 200
==================================
.. kernel-doc:: drivers/gpu/drm/tve200/tve200_drv.c
:doc: Faraday TV Encoder 200

View File

@ -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/

View File

@ -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;

View File

@ -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

View File

@ -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/

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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)

View 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 *,

View File

@ -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>

View File

@ -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)

View File

@ -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>

View File

@ -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;

View File

@ -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,

View File

@ -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;
}

View File

@ -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) {

View File

@ -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 @@ out:
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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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 @@ out:
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);

View File

@ -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;

View File

@ -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 @@ fail_len:
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 @@ fail_put:
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)

View File

@ -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;

View File

@ -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",

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 @@ retry:
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 @@ retry:
}
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 @@ out:
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);

View File

@ -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 @@ retry:
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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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>

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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 },
{ }
};

View File

@ -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 },
{ }
};

View File

@ -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 },
{ }
};

View File

@ -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);

View File

@ -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:

View File

@ -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,

View File

@ -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,

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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,
};

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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;

View File

@ -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_ */

View File

@ -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},
};

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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),
},
};

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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,

View File

@ -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) {

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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", &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 = {

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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