Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

Summaries:
- Add MIPI-DSI Driver, and dt bindigs
- Add S6E8AA0 MIPI-DSI based panel drivers, and dt bindings
- Add LD9040 parallel panel driver
 . this driver is placed in drivers/gpu/drm/panel, and it seems
   to be used for exynos drm as of now,
- Some fixups

Changelog v2:
- Remove super device support, and relevant dt bindings for more reviews.
- Fix module build errors you pointed out.
- Re-based it to drm-next again.

* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
  drm/bridge: export ptn3460_init function
  drm/exynos: remove MODULE_DEVICE_TABLE definitions
  ARM: dts: exynos4412-trats2: enable exynos/fimd node
  ARM: dts: exynos4210-trats: enable exynos/fimd node
  ARM: dts: exynos4412-trats2: add panel node
  ARM: dts: exynos4210-trats: add panel node
  ARM: dts: exynos4: add MIPI DSI Master node
  drm/panel: add S6E8AA0 driver
  ARM: dts: exynos4210-universal_c210: add proper panel node
  drm/panel: add ld9040 driver
  panel/ld9040: add DT bindings
  panel/s6e8aa0: add DT bindings
  drm/exynos: add DSIM driver
  exynos/dsim: add DT bindings
  drm/exynos: disallow fbdev initialization if no device is connected
  drm/mipi_dsi: create dsi devices only for nodes with reg property
  drm/mipi_dsi: add flags to DSI messages
This commit is contained in:
Dave Airlie 2014-04-05 16:09:15 +10:00
commit 13b9389274
21 changed files with 3445 additions and 19 deletions

View file

@ -0,0 +1,66 @@
Samsung LD9040 AMOLED LCD parallel RGB panel with SPI control bus
Required properties:
- compatible: "samsung,ld9040"
- reg: address of the panel on SPI bus
- vdd3-supply: core voltage supply
- vci-supply: voltage supply for analog circuits
- reset-gpios: a GPIO spec for the reset pin
- display-timings: timings for the connected panel according to [1]
The panel must obey rules for SPI slave device specified in document [2].
Optional properties:
- power-on-delay: delay after turning regulators on [ms]
- reset-delay: delay after reset sequence [ms]
- panel-width-mm: physical panel width [mm]
- panel-height-mm: physical panel height [mm]
The device node can contain one 'port' child node with one child
'endpoint' node, according to the bindings defined in [3]. This
node should describe panel's video bus.
[1]: Documentation/devicetree/bindings/video/display-timing.txt
[2]: Documentation/devicetree/bindings/spi/spi-bus.txt
[3]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
lcd@0 {
compatible = "samsung,ld9040";
reg = <0>;
vdd3-supply = <&ldo7_reg>;
vci-supply = <&ldo17_reg>;
reset-gpios = <&gpy4 5 0>;
spi-max-frequency = <1200000>;
spi-cpol;
spi-cpha;
power-on-delay = <10>;
reset-delay = <10>;
panel-width-mm = <90>;
panel-height-mm = <154>;
display-timings {
timing {
clock-frequency = <23492370>;
hactive = <480>;
vactive = <800>;
hback-porch = <16>;
hfront-porch = <16>;
vback-porch = <2>;
vfront-porch = <28>;
hsync-len = <2>;
vsync-len = <1>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <0>;
};
};
port {
lcd_ep: endpoint {
remote-endpoint = <&fimd_dpi_ep>;
};
};
};

View file

@ -0,0 +1,56 @@
Samsung S6E8AA0 AMOLED LCD 5.3 inch panel
Required properties:
- compatible: "samsung,s6e8aa0"
- reg: the virtual channel number of a DSI peripheral
- vdd3-supply: core voltage supply
- vci-supply: voltage supply for analog circuits
- reset-gpios: a GPIO spec for the reset pin
- display-timings: timings for the connected panel as described by [1]
Optional properties:
- power-on-delay: delay after turning regulators on [ms]
- reset-delay: delay after reset sequence [ms]
- init-delay: delay after initialization sequence [ms]
- panel-width-mm: physical panel width [mm]
- panel-height-mm: physical panel height [mm]
- flip-horizontal: boolean to flip image horizontally
- flip-vertical: boolean to flip image vertically
The device node can contain one 'port' child node with one child
'endpoint' node, according to the bindings defined in [2]. This
node should describe panel's video bus.
[1]: Documentation/devicetree/bindings/video/display-timing.txt
[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
panel {
compatible = "samsung,s6e8aa0";
reg = <0>;
vdd3-supply = <&vcclcd_reg>;
vci-supply = <&vlcd_reg>;
reset-gpios = <&gpy4 5 0>;
power-on-delay= <50>;
reset-delay = <100>;
init-delay = <100>;
panel-width-mm = <58>;
panel-height-mm = <103>;
flip-horizontal;
flip-vertical;
display-timings {
timing0: timing-0 {
clock-frequency = <57153600>;
hactive = <720>;
vactive = <1280>;
hfront-porch = <5>;
hback-porch = <5>;
hsync-len = <5>;
vfront-porch = <13>;
vback-porch = <1>;
vsync-len = <2>;
};
};
};

View file

@ -0,0 +1,80 @@
Exynos MIPI DSI Master
Required properties:
- compatible: "samsung,exynos4210-mipi-dsi"
- reg: physical base address and length of the registers set for the device
- interrupts: should contain DSI interrupt
- clocks: list of clock specifiers, must contain an entry for each required
entry in clock-names
- clock-names: should include "bus_clk"and "pll_clk" entries
- phys: list of phy specifiers, must contain an entry for each required
entry in phy-names
- phy-names: should include "dsim" entry
- vddcore-supply: MIPI DSIM Core voltage supply (e.g. 1.1V)
- vddio-supply: MIPI DSIM I/O and PLL voltage supply (e.g. 1.8V)
- samsung,pll-clock-frequency: specifies frequency of the "pll_clk" clock
- #address-cells, #size-cells: should be set respectively to <1> and <0>
according to DSI host bindings (see MIPI DSI bindings [1])
Optional properties:
- samsung,power-domain: a phandle to DSIM power domain node
Child nodes:
Should contain DSI peripheral nodes (see MIPI DSI bindings [1]).
Video interfaces:
Device node can contain video interface port nodes according to [2].
The following are properties specific to those nodes:
port node:
- reg: (required) can be 0 for input RGB/I80 port or 1 for DSI port;
endpoint node of DSI port (reg = 1):
- samsung,burst-clock-frequency: specifies DSI frequency in high-speed burst
mode
- samsung,esc-clock-frequency: specifies DSI frequency in escape mode
[1]: Documentation/devicetree/bindings/mipi/dsi/mipi-dsi-bus.txt
[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
dsi@11C80000 {
compatible = "samsung,exynos4210-mipi-dsi";
reg = <0x11C80000 0x10000>;
interrupts = <0 79 0>;
clocks = <&clock 286>, <&clock 143>;
clock-names = "bus_clk", "pll_clk";
phys = <&mipi_phy 1>;
phy-names = "dsim";
vddcore-supply = <&vusb_reg>;
vddio-supply = <&vmipi_reg>;
samsung,power-domain = <&pd_lcd0>;
#address-cells = <1>;
#size-cells = <0>;
samsung,pll-clock-frequency = <24000000>;
panel@1 {
reg = <0>;
...
port {
panel_ep: endpoint {
remote-endpoint = <&dsi_ep>;
};
};
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
dsi_ep: endpoint {
reg = <0>;
samsung,burst-clock-frequency = <500000000>;
samsung,esc-clock-frequency = <20000000>;
remote-endpoint = <&panel_ep>;
};
};
};
};

View file

@ -104,6 +104,20 @@
reg = <0x10010000 0x400>;
};
dsi_0: dsi@11C80000 {
compatible = "samsung,exynos4210-mipi-dsi";
reg = <0x11C80000 0x10000>;
interrupts = <0 79 0>;
samsung,power-domain = <&pd_lcd0>;
phys = <&mipi_phy 1>;
phy-names = "dsim";
clocks = <&clock 286>, <&clock 143>;
clock-names = "bus_clk", "pll_clk";
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
};
camera {
compatible = "samsung,fimc", "simple-bus";
status = "disabled";

View file

@ -353,6 +353,67 @@
};
};
dsi_0: dsi@11C80000 {
vddcore-supply = <&vusb_reg>;
vddio-supply = <&vmipi_reg>;
samsung,pll-clock-frequency = <24000000>;
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
reg = <1>;
dsi_out: endpoint {
remote-endpoint = <&dsi_in>;
samsung,burst-clock-frequency = <500000000>;
samsung,esc-clock-frequency = <20000000>;
};
};
};
panel@0 {
reg = <0>;
compatible = "samsung,s6e8aa0";
vdd3-supply = <&vcclcd_reg>;
vci-supply = <&vlcd_reg>;
reset-gpios = <&gpy4 5 0>;
power-on-delay= <50>;
reset-delay = <100>;
init-delay = <100>;
flip-horizontal;
flip-vertical;
panel-width-mm = <58>;
panel-height-mm = <103>;
display-timings {
timing-0 {
clock-frequency = <57153600>;
hactive = <720>;
vactive = <1280>;
hfront-porch = <5>;
hback-porch = <5>;
hsync-len = <5>;
vfront-porch = <13>;
vback-porch = <1>;
vsync-len = <2>;
};
};
port {
dsi_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
};
};
fimd@11c00000 {
status = "okay";
};
camera {
pinctrl-names = "default";
pinctrl-0 = <>;

View file

@ -225,7 +225,6 @@
regulator-name = "VLCD+VMIPI_1.8V";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
};
ldo8_reg: LDO8 {
@ -289,7 +288,6 @@
regulator-name = "VCC_3.0V_LCD";
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
regulator-always-on;
};
buck1_reg: BUCK1 {
@ -347,27 +345,66 @@
};
};
spi-lcd {
compatible = "spi-gpio";
#address-cells = <1>;
#size-cells = <0>;
gpio-sck = <&gpy3 1 0>;
gpio-mosi = <&gpy3 3 0>;
num-chipselects = <1>;
cs-gpios = <&gpy4 3 0>;
lcd@0 {
compatible = "samsung,ld9040";
reg = <0>;
vdd3-supply = <&ldo7_reg>;
vci-supply = <&ldo17_reg>;
reset-gpios = <&gpy4 5 0>;
spi-max-frequency = <1200000>;
spi-cpol;
spi-cpha;
power-on-delay = <10>;
reset-delay = <10>;
panel-width-mm = <90>;
panel-height-mm = <154>;
display-timings {
timing {
clock-frequency = <23492370>;
hactive = <480>;
vactive = <800>;
hback-porch = <16>;
hfront-porch = <16>;
vback-porch = <2>;
vfront-porch = <28>;
hsync-len = <2>;
vsync-len = <1>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <0>;
};
};
port {
lcd_ep: endpoint {
remote-endpoint = <&fimd_dpi_ep>;
};
};
};
};
fimd: fimd@11c00000 {
pinctrl-0 = <&lcd_clk>, <&lcd_data24>;
pinctrl-names = "default";
status = "okay";
samsung,invert-vden;
samsung,invert-vclk;
display-timings {
timing {
clock-frequency = <23492370>;
hactive = <480>;
vactive = <800>;
hback-porch = <16>;
hfront-porch = <16>;
vback-porch = <2>;
vfront-porch = <28>;
hsync-len = <2>;
vsync-len = <1>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <0>;
#address-cells = <1>;
#size-cells = <0>;
port@3 {
reg = <3>;
fimd_dpi_ep: endpoint {
remote-endpoint = <&lcd_ep>;
};
};
};

View file

@ -71,6 +71,15 @@
enable-active-high;
};
lcd_vdd3_reg: voltage-regulator-2 {
compatible = "regulator-fixed";
regulator-name = "LCD_VDD_2.2V";
regulator-min-microvolt = <2200000>;
regulator-max-microvolt = <2200000>;
gpio = <&gpc0 1 0>;
enable-active-high;
};
/* More to come */
};
@ -511,6 +520,67 @@
};
};
dsi_0: dsi@11C80000 {
vddcore-supply = <&ldo8_reg>;
vddio-supply = <&ldo10_reg>;
samsung,pll-clock-frequency = <24000000>;
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
reg = <1>;
dsi_out: endpoint {
remote-endpoint = <&dsi_in>;
samsung,burst-clock-frequency = <500000000>;
samsung,esc-clock-frequency = <20000000>;
};
};
};
panel@0 {
compatible = "samsung,s6e8aa0";
reg = <0>;
vdd3-supply = <&lcd_vdd3_reg>;
vci-supply = <&ldo25_reg>;
reset-gpios = <&gpy4 5 0>;
power-on-delay= <50>;
reset-delay = <100>;
init-delay = <100>;
flip-horizontal;
flip-vertical;
panel-width-mm = <58>;
panel-height-mm = <103>;
display-timings {
timing-0 {
clock-frequency = <0>;
hactive = <720>;
vactive = <1280>;
hfront-porch = <5>;
hback-porch = <5>;
hsync-len = <5>;
vfront-porch = <13>;
vback-porch = <1>;
vsync-len = <2>;
};
};
port {
dsi_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
};
};
fimd@11c00000 {
status = "okay";
};
camera {
pinctrl-0 = <&cam_port_b_clk_active>;
pinctrl-names = "default";

View file

@ -347,3 +347,4 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
gpio_free(ptn_bridge->gpio_rst_n);
return ret;
}
EXPORT_SYMBOL(ptn3460_init);

View file

@ -142,8 +142,12 @@ int mipi_dsi_host_register(struct mipi_dsi_host *host)
{
struct device_node *node;
for_each_available_child_of_node(host->dev->of_node, node)
for_each_available_child_of_node(host->dev->of_node, node) {
/* skip nodes without reg property */
if (!of_find_property(node, "reg", NULL))
continue;
of_mipi_dsi_device_add(host, node);
}
return 0;
}

View file

@ -39,6 +39,15 @@ config DRM_EXYNOS_DPI
help
This enables support for Exynos parallel output.
config DRM_EXYNOS_DSI
bool "EXYNOS DRM MIPI-DSI driver support"
depends on DRM_EXYNOS
select DRM_MIPI_DSI
select DRM_PANEL
default n
help
This enables support for Exynos MIPI-DSI device.
config DRM_EXYNOS_DP
bool "EXYNOS DRM DP driver support"
depends on DRM_EXYNOS && ARCH_EXYNOS

View file

@ -12,6 +12,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DPI) += exynos_drm_dpi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DSI) += exynos_drm_dsi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o
exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o
exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o

View file

@ -1339,7 +1339,6 @@ static const struct of_device_id exynos_dp_match[] = {
{ .compatible = "samsung,exynos5-dp" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_dp_match);
struct platform_driver dp_driver = {
.probe = exynos_dp_probe,

View file

@ -450,6 +450,12 @@ static int __init exynos_drm_init(void)
goto out_dp;
#endif
#ifdef CONFIG_DRM_EXYNOS_DSI
ret = platform_driver_register(&dsi_driver);
if (ret < 0)
goto out_dsi;
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
ret = platform_driver_register(&fimd_driver);
if (ret < 0)
@ -566,6 +572,11 @@ static int __init exynos_drm_init(void)
out_fimd:
#endif
#ifdef CONFIG_DRM_EXYNOS_DSI
platform_driver_unregister(&dsi_driver);
out_dsi:
#endif
#ifdef CONFIG_DRM_EXYNOS_DP
platform_driver_unregister(&dp_driver);
out_dp:
@ -613,6 +624,10 @@ static void __exit exynos_drm_exit(void)
platform_driver_unregister(&fimd_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_DSI
platform_driver_unregister(&dsi_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_DP
platform_driver_unregister(&dp_driver);
#endif

View file

@ -370,6 +370,7 @@ static inline int exynos_dpi_remove(struct device *dev) { return 0; }
#endif
extern struct platform_driver dp_driver;
extern struct platform_driver dsi_driver;
extern struct platform_driver fimd_driver;
extern struct platform_driver hdmi_driver;
extern struct platform_driver mixer_driver;

File diff suppressed because it is too large Load diff

View file

@ -237,6 +237,24 @@ static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
.fb_probe = exynos_drm_fbdev_create,
};
bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
{
struct drm_connector *connector;
bool ret = false;
mutex_lock(&dev->mode_config.mutex);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->status != connector_status_connected)
continue;
ret = true;
break;
}
mutex_unlock(&dev->mode_config.mutex);
return ret;
}
int exynos_drm_fbdev_init(struct drm_device *dev)
{
struct exynos_drm_fbdev *fbdev;
@ -248,6 +266,9 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
return 0;
if (!exynos_drm_fbdev_is_anything_connected(dev))
return 0;
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
if (!fbdev)
return -ENOMEM;

View file

@ -16,4 +16,18 @@ config DRM_PANEL_SIMPLE
that it can be automatically turned off when the panel goes into a
low power state.
config DRM_PANEL_LD9040
tristate "LD9040 RGB/SPI panel"
depends on DRM && DRM_PANEL
depends on OF
select SPI
select VIDEOMODE_HELPERS
config DRM_PANEL_S6E8AA0
tristate "S6E8AA0 DSI video mode panel"
depends on DRM && DRM_PANEL
depends on OF
select DRM_MIPI_DSI
select VIDEOMODE_HELPERS
endmenu

View file

@ -1 +1,3 @@
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o
obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o

View file

@ -0,0 +1,376 @@
/*
* ld9040 AMOLED LCD drm_panel driver.
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd
* Derived from drivers/video/backlight/ld9040.c
*
* Andrzej Hajda <a.hajda@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_panel.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <video/mipi_display.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
/* Manufacturer Command Set */
#define MCS_MANPWR 0xb0
#define MCS_ELVSS_ON 0xb1
#define MCS_USER_SETTING 0xf0
#define MCS_DISPCTL 0xf2
#define MCS_GTCON 0xf7
#define MCS_PANEL_CONDITION 0xf8
#define MCS_GAMMA_SET1 0xf9
#define MCS_GAMMA_CTRL 0xfb
/* array of gamma tables for gamma value 2.2 */
static u8 const ld9040_gammas[25][22] = {
{ 0xf9, 0x00, 0x13, 0xb2, 0xba, 0xd2, 0x00, 0x30, 0x00, 0xaf, 0xc0,
0xb8, 0xcd, 0x00, 0x3d, 0x00, 0xa8, 0xb8, 0xb7, 0xcd, 0x00, 0x44 },
{ 0xf9, 0x00, 0x13, 0xb9, 0xb9, 0xd0, 0x00, 0x3c, 0x00, 0xaf, 0xbf,
0xb6, 0xcb, 0x00, 0x4b, 0x00, 0xa8, 0xb9, 0xb5, 0xcc, 0x00, 0x52 },
{ 0xf9, 0x00, 0x13, 0xba, 0xb9, 0xcd, 0x00, 0x41, 0x00, 0xb0, 0xbe,
0xb5, 0xc9, 0x00, 0x51, 0x00, 0xa9, 0xb9, 0xb5, 0xca, 0x00, 0x57 },
{ 0xf9, 0x00, 0x13, 0xb9, 0xb8, 0xcd, 0x00, 0x46, 0x00, 0xb1, 0xbc,
0xb5, 0xc8, 0x00, 0x56, 0x00, 0xaa, 0xb8, 0xb4, 0xc9, 0x00, 0x5d },
{ 0xf9, 0x00, 0x13, 0xba, 0xb8, 0xcb, 0x00, 0x4b, 0x00, 0xb3, 0xbc,
0xb4, 0xc7, 0x00, 0x5c, 0x00, 0xac, 0xb8, 0xb4, 0xc8, 0x00, 0x62 },
{ 0xf9, 0x00, 0x13, 0xbb, 0xb7, 0xca, 0x00, 0x4f, 0x00, 0xb4, 0xbb,
0xb3, 0xc7, 0x00, 0x60, 0x00, 0xad, 0xb8, 0xb4, 0xc7, 0x00, 0x67 },
{ 0xf9, 0x00, 0x47, 0xba, 0xb6, 0xca, 0x00, 0x53, 0x00, 0xb5, 0xbb,
0xb3, 0xc6, 0x00, 0x65, 0x00, 0xae, 0xb8, 0xb3, 0xc7, 0x00, 0x6c },
{ 0xf9, 0x00, 0x71, 0xbb, 0xb5, 0xc8, 0x00, 0x57, 0x00, 0xb5, 0xbb,
0xb0, 0xc5, 0x00, 0x6a, 0x00, 0xae, 0xb9, 0xb1, 0xc6, 0x00, 0x70 },
{ 0xf9, 0x00, 0x7b, 0xbb, 0xb4, 0xc8, 0x00, 0x5b, 0x00, 0xb5, 0xba,
0xb1, 0xc4, 0x00, 0x6e, 0x00, 0xae, 0xb9, 0xb0, 0xc5, 0x00, 0x75 },
{ 0xf9, 0x00, 0x82, 0xba, 0xb4, 0xc7, 0x00, 0x5f, 0x00, 0xb5, 0xba,
0xb0, 0xc3, 0x00, 0x72, 0x00, 0xae, 0xb8, 0xb0, 0xc3, 0x00, 0x7a },
{ 0xf9, 0x00, 0x89, 0xba, 0xb3, 0xc8, 0x00, 0x62, 0x00, 0xb6, 0xba,
0xaf, 0xc3, 0x00, 0x76, 0x00, 0xaf, 0xb7, 0xae, 0xc4, 0x00, 0x7e },
{ 0xf9, 0x00, 0x8b, 0xb9, 0xb3, 0xc7, 0x00, 0x65, 0x00, 0xb7, 0xb8,
0xaf, 0xc3, 0x00, 0x7a, 0x00, 0x80, 0xb6, 0xae, 0xc4, 0x00, 0x81 },
{ 0xf9, 0x00, 0x93, 0xba, 0xb3, 0xc5, 0x00, 0x69, 0x00, 0xb8, 0xb9,
0xae, 0xc1, 0x00, 0x7f, 0x00, 0xb0, 0xb6, 0xae, 0xc3, 0x00, 0x85 },
{ 0xf9, 0x00, 0x97, 0xba, 0xb2, 0xc5, 0x00, 0x6c, 0x00, 0xb8, 0xb8,
0xae, 0xc1, 0x00, 0x82, 0x00, 0xb0, 0xb6, 0xae, 0xc2, 0x00, 0x89 },
{ 0xf9, 0x00, 0x9a, 0xba, 0xb1, 0xc4, 0x00, 0x6f, 0x00, 0xb8, 0xb8,
0xad, 0xc0, 0x00, 0x86, 0x00, 0xb0, 0xb7, 0xad, 0xc0, 0x00, 0x8d },
{ 0xf9, 0x00, 0x9c, 0xb9, 0xb0, 0xc4, 0x00, 0x72, 0x00, 0xb8, 0xb8,
0xac, 0xbf, 0x00, 0x8a, 0x00, 0xb0, 0xb6, 0xac, 0xc0, 0x00, 0x91 },
{ 0xf9, 0x00, 0x9e, 0xba, 0xb0, 0xc2, 0x00, 0x75, 0x00, 0xb9, 0xb8,
0xab, 0xbe, 0x00, 0x8e, 0x00, 0xb0, 0xb6, 0xac, 0xbf, 0x00, 0x94 },
{ 0xf9, 0x00, 0xa0, 0xb9, 0xaf, 0xc3, 0x00, 0x77, 0x00, 0xb9, 0xb7,
0xab, 0xbe, 0x00, 0x90, 0x00, 0xb0, 0xb6, 0xab, 0xbf, 0x00, 0x97 },
{ 0xf9, 0x00, 0xa2, 0xb9, 0xaf, 0xc2, 0x00, 0x7a, 0x00, 0xb9, 0xb7,
0xaa, 0xbd, 0x00, 0x94, 0x00, 0xb0, 0xb5, 0xab, 0xbf, 0x00, 0x9a },
{ 0xf9, 0x00, 0xa4, 0xb9, 0xaf, 0xc1, 0x00, 0x7d, 0x00, 0xb9, 0xb6,
0xaa, 0xbb, 0x00, 0x97, 0x00, 0xb1, 0xb5, 0xaa, 0xbf, 0x00, 0x9d },
{ 0xf9, 0x00, 0xa4, 0xb8, 0xb0, 0xbf, 0x00, 0x80, 0x00, 0xb8, 0xb6,
0xaa, 0xbc, 0x00, 0x9a, 0x00, 0xb0, 0xb5, 0xab, 0xbd, 0x00, 0xa0 },
{ 0xf9, 0x00, 0xa8, 0xb8, 0xae, 0xbe, 0x00, 0x84, 0x00, 0xb9, 0xb7,
0xa8, 0xbc, 0x00, 0x9d, 0x00, 0xb2, 0xb5, 0xaa, 0xbc, 0x00, 0xa4 },
{ 0xf9, 0x00, 0xa9, 0xb6, 0xad, 0xbf, 0x00, 0x86, 0x00, 0xb8, 0xb5,
0xa8, 0xbc, 0x00, 0xa0, 0x00, 0xb3, 0xb3, 0xa9, 0xbc, 0x00, 0xa7 },
{ 0xf9, 0x00, 0xa9, 0xb7, 0xae, 0xbd, 0x00, 0x89, 0x00, 0xb7, 0xb6,
0xa8, 0xba, 0x00, 0xa4, 0x00, 0xb1, 0xb4, 0xaa, 0xbb, 0x00, 0xaa },
{ 0xf9, 0x00, 0xa7, 0xb4, 0xae, 0xbf, 0x00, 0x91, 0x00, 0xb2, 0xb4,
0xaa, 0xbb, 0x00, 0xac, 0x00, 0xb3, 0xb1, 0xaa, 0xbc, 0x00, 0xb3 },
};
struct ld9040 {
struct device *dev;
struct drm_panel panel;
struct regulator_bulk_data supplies[2];
struct gpio_desc *reset_gpio;
u32 power_on_delay;
u32 reset_delay;
struct videomode vm;
u32 width_mm;
u32 height_mm;
int brightness;
/* This field is tested by functions directly accessing bus before
* transfer, transfer is skipped if it is set. In case of transfer
* failure or unexpected response the field is set to error value.
* Such construct allows to eliminate many checks in higher level
* functions.
*/
int error;
};
#define panel_to_ld9040(p) container_of(p, struct ld9040, panel)
static int ld9040_clear_error(struct ld9040 *ctx)
{
int ret = ctx->error;
ctx->error = 0;
return ret;
}
static int ld9040_spi_write_word(struct ld9040 *ctx, u16 data)
{
struct spi_device *spi = to_spi_device(ctx->dev);
struct spi_transfer xfer = {
.len = 2,
.tx_buf = &data,
};
struct spi_message msg;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
return spi_sync(spi, &msg);
}
static void ld9040_dcs_write(struct ld9040 *ctx, const u8 *data, size_t len)
{
int ret = 0;
if (ctx->error < 0 || len == 0)
return;
dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", len, data);
ret = ld9040_spi_write_word(ctx, *data);
while (!ret && --len) {
++data;
ret = ld9040_spi_write_word(ctx, *data | 0x100);
}
if (ret) {
dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len,
data);
ctx->error = ret;
}
usleep_range(300, 310);
}
#define ld9040_dcs_write_seq_static(ctx, seq...) \
({\
static const u8 d[] = { seq };\
ld9040_dcs_write(ctx, d, ARRAY_SIZE(d));\
})
static void ld9040_brightness_set(struct ld9040 *ctx)
{
ld9040_dcs_write(ctx, ld9040_gammas[ctx->brightness],
ARRAY_SIZE(ld9040_gammas[ctx->brightness]));
ld9040_dcs_write_seq_static(ctx, MCS_GAMMA_CTRL, 0x02, 0x5a);
}
static void ld9040_init(struct ld9040 *ctx)
{
ld9040_dcs_write_seq_static(ctx, MCS_USER_SETTING, 0x5a, 0x5a);
ld9040_dcs_write_seq_static(ctx, MCS_PANEL_CONDITION,
0x05, 0x65, 0x96, 0x71, 0x7d, 0x19, 0x3b, 0x0d,
0x19, 0x7e, 0x0d, 0xe2, 0x00, 0x00, 0x7e, 0x7d,
0x07, 0x07, 0x20, 0x20, 0x20, 0x02, 0x02);
ld9040_dcs_write_seq_static(ctx, MCS_DISPCTL,
0x02, 0x08, 0x08, 0x10, 0x10);
ld9040_dcs_write_seq_static(ctx, MCS_MANPWR, 0x04);
ld9040_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 0x0d, 0x00, 0x16);
ld9040_dcs_write_seq_static(ctx, MCS_GTCON, 0x09, 0x00, 0x00);
ld9040_brightness_set(ctx);
ld9040_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
ld9040_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
}
static int ld9040_power_on(struct ld9040 *ctx)
{
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
if (ret < 0)
return ret;
msleep(ctx->power_on_delay);
gpiod_set_value(ctx->reset_gpio, 0);
msleep(ctx->reset_delay);
gpiod_set_value(ctx->reset_gpio, 1);
msleep(ctx->reset_delay);
return 0;
}
static int ld9040_power_off(struct ld9040 *ctx)
{
return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
}
static int ld9040_disable(struct drm_panel *panel)
{
struct ld9040 *ctx = panel_to_ld9040(panel);
msleep(120);
ld9040_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
ld9040_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
msleep(40);
ld9040_clear_error(ctx);
return ld9040_power_off(ctx);
}
static int ld9040_enable(struct drm_panel *panel)
{
struct ld9040 *ctx = panel_to_ld9040(panel);
int ret;
ret = ld9040_power_on(ctx);
if (ret < 0)
return ret;
ld9040_init(ctx);
ret = ld9040_clear_error(ctx);
if (ret < 0)
ld9040_disable(panel);
return ret;
}
static int ld9040_get_modes(struct drm_panel *panel)
{
struct drm_connector *connector = panel->connector;
struct ld9040 *ctx = panel_to_ld9040(panel);
struct drm_display_mode *mode;
mode = drm_mode_create(connector->dev);
if (!mode) {
DRM_ERROR("failed to create a new display mode\n");
return 0;
}
drm_display_mode_from_videomode(&ctx->vm, mode);
mode->width_mm = ctx->width_mm;
mode->height_mm = ctx->height_mm;
connector->display_info.width_mm = mode->width_mm;
connector->display_info.height_mm = mode->height_mm;
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
return 1;
}
static const struct drm_panel_funcs ld9040_drm_funcs = {
.disable = ld9040_disable,
.enable = ld9040_enable,
.get_modes = ld9040_get_modes,
};
static int ld9040_parse_dt(struct ld9040 *ctx)
{
struct device *dev = ctx->dev;
struct device_node *np = dev->of_node;
int ret;
ret = of_get_videomode(np, &ctx->vm, 0);
if (ret < 0)
return ret;
of_property_read_u32(np, "power-on-delay", &ctx->power_on_delay);
of_property_read_u32(np, "reset-delay", &ctx->reset_delay);
of_property_read_u32(np, "panel-width-mm", &ctx->width_mm);
of_property_read_u32(np, "panel-height-mm", &ctx->height_mm);
return 0;
}
static int ld9040_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct ld9040 *ctx;
int ret;
ctx = devm_kzalloc(dev, sizeof(struct ld9040), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
spi_set_drvdata(spi, ctx);
ctx->dev = dev;
ctx->brightness = ARRAY_SIZE(ld9040_gammas) - 1;
ret = ld9040_parse_dt(ctx);
if (ret < 0)
return ret;
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)
return ret;
ctx->reset_gpio = devm_gpiod_get(dev, "reset");
if (IS_ERR(ctx->reset_gpio)) {
dev_err(dev, "cannot get reset-gpios %ld\n",
PTR_ERR(ctx->reset_gpio));
return PTR_ERR(ctx->reset_gpio);
}
ret = gpiod_direction_output(ctx->reset_gpio, 1);
if (ret < 0) {
dev_err(dev, "cannot configure reset-gpios %d\n", ret);
return ret;
}
spi->bits_per_word = 9;
ret = spi_setup(spi);
if (ret < 0) {
dev_err(dev, "spi setup failed.\n");
return ret;
}
drm_panel_init(&ctx->panel);
ctx->panel.dev = dev;
ctx->panel.funcs = &ld9040_drm_funcs;
return drm_panel_add(&ctx->panel);
}
static int ld9040_remove(struct spi_device *spi)
{
struct ld9040 *ctx = spi_get_drvdata(spi);
ld9040_power_off(ctx);
drm_panel_remove(&ctx->panel);
return 0;
}
static struct of_device_id ld9040_of_match[] = {
{ .compatible = "samsung,ld9040" },
{ }
};
MODULE_DEVICE_TABLE(of, ld9040_of_match);
static struct spi_driver ld9040_driver = {
.probe = ld9040_probe,
.remove = ld9040_remove,
.driver = {
.name = "ld9040",
.owner = THIS_MODULE,
.of_match_table = ld9040_of_match,
},
};
module_spi_driver(ld9040_driver);
MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
MODULE_DESCRIPTION("ld9040 LCD Driver");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,11 @@
struct mipi_dsi_host;
struct mipi_dsi_device;
/* request ACK from peripheral */
#define MIPI_DSI_MSG_REQ_ACK BIT(0)
/* use Low Power Mode to transmit message */
#define MIPI_DSI_MSG_USE_LPM BIT(1)
/**
* struct mipi_dsi_msg - read/write DSI buffer
* @channel: virtual channel id
@ -29,6 +34,7 @@ struct mipi_dsi_device;
struct mipi_dsi_msg {
u8 channel;
u8 type;
u16 flags;
size_t tx_len;
const void *tx_buf;