aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/display/ilitek,ili9225.txt4
-rw-r--r--Documentation/devicetree/bindings/display/panel/ilitek,ili9322.txt49
-rw-r--r--Documentation/devicetree/bindings/display/panel/panel-common.txt10
-rw-r--r--Documentation/devicetree/bindings/display/panel/panel-lvds.txt1
-rw-r--r--Documentation/devicetree/bindings/display/panel/simple-panel.txt2
-rw-r--r--Documentation/devicetree/bindings/display/sitronix,st7735r.txt35
-rw-r--r--Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt11
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt2
-rw-r--r--MAINTAINERS6
-rw-r--r--drivers/gpu/drm/drm_panel_orientation_quirks.c3
-rw-r--r--drivers/gpu/drm/panel/Kconfig8
-rw-r--r--drivers/gpu/drm/panel/Makefile1
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9322.c962
-rw-r--r--drivers/gpu/drm/panel/panel-lvds.c23
-rw-r--r--drivers/gpu/drm/sun4i/Makefile1
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_dotclock.c10
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.c1
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_lvds.c177
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_lvds.h12
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.c244
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.h31
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_mixer.c21
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_mixer.h3
-rw-r--r--drivers/gpu/drm/tinydrm/Kconfig10
-rw-r--r--drivers/gpu/drm/tinydrm/Makefile1
-rw-r--r--drivers/gpu/drm/tinydrm/ili9225.c4
-rw-r--r--drivers/gpu/drm/tinydrm/st7735r.c215
27 files changed, 1838 insertions, 9 deletions
diff --git a/Documentation/devicetree/bindings/display/ilitek,ili9225.txt b/Documentation/devicetree/bindings/display/ilitek,ili9225.txt
index 21607a541c33..a59feb52015b 100644
--- a/Documentation/devicetree/bindings/display/ilitek,ili9225.txt
+++ b/Documentation/devicetree/bindings/display/ilitek,ili9225.txt
@@ -4,7 +4,7 @@ This binding is for display panels using an Ilitek ILI9225 controller in SPI
4mode. 4mode.
5 5
6Required properties: 6Required properties:
7- compatible: "ilitek,ili9225-2.2in-176x220" 7- compatible: "vot,v220hf01a-t", "ilitek,ili9225"
8- rs-gpios: Register select signal 8- rs-gpios: Register select signal
9- reset-gpios: Reset pin 9- reset-gpios: Reset pin
10 10
@@ -16,7 +16,7 @@ Optional properties:
16 16
17Example: 17Example:
18 display@0{ 18 display@0{
19 compatible = "ilitek,ili9225-2.2in-176x220"; 19 compatible = "vot,v220hf01a-t", "ilitek,ili9225";
20 reg = <0>; 20 reg = <0>;
21 spi-max-frequency = <12000000>; 21 spi-max-frequency = <12000000>;
22 rs-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; 22 rs-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9322.txt b/Documentation/devicetree/bindings/display/panel/ilitek,ili9322.txt
new file mode 100644
index 000000000000..3d5ce6ad6ec7
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9322.txt
@@ -0,0 +1,49 @@
1Ilitek ILI9322 TFT panel driver with SPI control bus
2
3This is a driver for 320x240 TFT panels, accepting a variety of input
4streams that get adapted and scaled to the panel. The panel output has
5960 TFT source driver pins and 240 TFT gate driver pins, VCOM, VCOML and
6VCOMH outputs.
7
8Required properties:
9 - compatible: "dlink,dir-685-panel", "ilitek,ili9322"
10 (full system-specific compatible is always required to look up configuration)
11 - reg: address of the panel on the SPI bus
12
13Optional properties:
14 - vcc-supply: core voltage supply, see regulator/regulator.txt
15 - iovcc-supply: voltage supply for the interface input/output signals,
16 see regulator/regulator.txt
17 - vci-supply: voltage supply for analog parts, see regulator/regulator.txt
18 - reset-gpios: a GPIO spec for the reset pin, see gpio/gpio.txt
19
20 The following optional properties only apply to RGB and YUV input modes and
21 can be omitted for BT.656 input modes:
22
23 - pixelclk-active: see display/panel/display-timing.txt
24 - de-active: see display/panel/display-timing.txt
25 - hsync-active: see display/panel/display-timing.txt
26 - vsync-active: see display/panel/display-timing.txt
27
28The panel must obey the rules for a SPI slave device as specified in
29spi/spi-bus.txt
30
31The device node can contain one 'port' child node with one child
32'endpoint' node, according to the bindings defined in
33media/video-interfaces.txt. This node should describe panel's video bus.
34
35Example:
36
37panel: display@0 {
38 compatible = "dlink,dir-685-panel", "ilitek,ili9322";
39 reg = <0>;
40 vcc-supply = <&vdisp>;
41 iovcc-supply = <&vdisp>;
42 vci-supply = <&vdisp>;
43
44 port {
45 panel_in: endpoint {
46 remote-endpoint = <&display_out>;
47 };
48 };
49};
diff --git a/Documentation/devicetree/bindings/display/panel/panel-common.txt b/Documentation/devicetree/bindings/display/panel/panel-common.txt
index ec52c472c845..557fa765adcb 100644
--- a/Documentation/devicetree/bindings/display/panel/panel-common.txt
+++ b/Documentation/devicetree/bindings/display/panel/panel-common.txt
@@ -78,6 +78,16 @@ used for panels that implement compatible control signals.
78 while active. Active high reset signals can be supported by inverting the 78 while active. Active high reset signals can be supported by inverting the
79 GPIO specifier polarity flag. 79 GPIO specifier polarity flag.
80 80
81Power
82-----
83
84- power-supply: display panels require power to be supplied. While several
85 panels need more than one power supply with panel-specific constraints
86 governing the order and timings of the power supplies, in many cases a single
87 power supply is sufficient, either because the panel has a single power rail,
88 or because all its power rails can be driven by the same supply. In that case
89 the power-supply property specifies the supply powering the panel as a phandle
90 to a regulator.
81 91
82Backlight 92Backlight
83--------- 93---------
diff --git a/Documentation/devicetree/bindings/display/panel/panel-lvds.txt b/Documentation/devicetree/bindings/display/panel/panel-lvds.txt
index b938269f841e..250850a2150b 100644
--- a/Documentation/devicetree/bindings/display/panel/panel-lvds.txt
+++ b/Documentation/devicetree/bindings/display/panel/panel-lvds.txt
@@ -32,6 +32,7 @@ Optional properties:
32- label: See panel-common.txt. 32- label: See panel-common.txt.
33- gpios: See panel-common.txt. 33- gpios: See panel-common.txt.
34- backlight: See panel-common.txt. 34- backlight: See panel-common.txt.
35- power-supply: See panel-common.txt.
35- data-mirror: If set, reverse the bit order described in the data mappings 36- data-mirror: If set, reverse the bit order described in the data mappings
36 below on all data lanes, transmitting bits for slots 6 to 0 instead of 37 below on all data lanes, transmitting bits for slots 6 to 0 instead of
37 0 to 6. 38 0 to 6.
diff --git a/Documentation/devicetree/bindings/display/panel/simple-panel.txt b/Documentation/devicetree/bindings/display/panel/simple-panel.txt
index 1341bbf4aa3d..16d8ff088b7d 100644
--- a/Documentation/devicetree/bindings/display/panel/simple-panel.txt
+++ b/Documentation/devicetree/bindings/display/panel/simple-panel.txt
@@ -1,7 +1,7 @@
1Simple display panel 1Simple display panel
2 2
3Required properties: 3Required properties:
4- power-supply: regulator to provide the supply voltage 4- power-supply: See panel-common.txt
5 5
6Optional properties: 6Optional properties:
7- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing 7- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
diff --git a/Documentation/devicetree/bindings/display/sitronix,st7735r.txt b/Documentation/devicetree/bindings/display/sitronix,st7735r.txt
new file mode 100644
index 000000000000..f0a5090a3326
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sitronix,st7735r.txt
@@ -0,0 +1,35 @@
1Sitronix ST7735R display panels
2
3This binding is for display panels using a Sitronix ST7735R controller in SPI
4mode.
5
6Required properties:
7- compatible: "jianda,jd-t18003-t01", "sitronix,st7735r"
8- dc-gpios: Display data/command selection (D/CX)
9- reset-gpios: Reset signal (RSTX)
10
11The node for this driver must be a child node of a SPI controller, hence
12all mandatory properties described in ../spi/spi-bus.txt must be specified.
13
14Optional properties:
15- rotation: panel rotation in degrees counter clockwise (0,90,180,270)
16- backlight: phandle of the backlight device attached to the panel
17
18Example:
19
20 backlight: backlight {
21 compatible = "gpio-backlight";
22 gpios = <&gpio 44 GPIO_ACTIVE_HIGH>;
23 }
24
25 ...
26
27 display@0{
28 compatible = "jianda,jd-t18003-t01", "sitronix,st7735r";
29 reg = <0>;
30 spi-max-frequency = <32000000>;
31 dc-gpios = <&gpio 43 GPIO_ACTIVE_HIGH>;
32 reset-gpios = <&gpio 80 GPIO_ACTIVE_HIGH>;
33 rotation = <270>;
34 backlight = &backlight;
35 };
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index 50cc72ee1168..cd626ee1147a 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -93,6 +93,7 @@ Required properties:
93 * allwinner,sun6i-a31s-tcon 93 * allwinner,sun6i-a31s-tcon
94 * allwinner,sun7i-a20-tcon 94 * allwinner,sun7i-a20-tcon
95 * allwinner,sun8i-a33-tcon 95 * allwinner,sun8i-a33-tcon
96 * allwinner,sun8i-a83t-tcon-lcd
96 * allwinner,sun8i-v3s-tcon 97 * allwinner,sun8i-v3s-tcon
97 - reg: base address and size of memory-mapped region 98 - reg: base address and size of memory-mapped region
98 - interrupts: interrupt associated to this IP 99 - interrupts: interrupt associated to this IP
@@ -121,6 +122,14 @@ Required properties:
121On SoCs other than the A33 and V3s, there is one more clock required: 122On SoCs other than the A33 and V3s, there is one more clock required:
122 - 'tcon-ch1': The clock driving the TCON channel 1 123 - 'tcon-ch1': The clock driving the TCON channel 1
123 124
125On SoCs that support LVDS (all SoCs but the A13, H3, H5 and V3s), you
126need one more reset line:
127 - 'lvds': The reset line driving the LVDS logic
128
129And on the A23, A31, A31s and A33, you need one more clock line:
130 - 'lvds-alt': An alternative clock source, separate from the TCON channel 0
131 clock, that can be used to drive the LVDS clock
132
124DRC 133DRC
125--- 134---
126 135
@@ -216,6 +225,7 @@ supported.
216 225
217Required properties: 226Required properties:
218 - compatible: value must be one of: 227 - compatible: value must be one of:
228 * allwinner,sun8i-a83t-de2-mixer-0
219 * allwinner,sun8i-v3s-de2-mixer 229 * allwinner,sun8i-v3s-de2-mixer
220 - reg: base address and size of the memory-mapped region. 230 - reg: base address and size of the memory-mapped region.
221 - clocks: phandles to the clocks feeding the mixer 231 - clocks: phandles to the clocks feeding the mixer
@@ -245,6 +255,7 @@ Required properties:
245 * allwinner,sun6i-a31s-display-engine 255 * allwinner,sun6i-a31s-display-engine
246 * allwinner,sun7i-a20-display-engine 256 * allwinner,sun7i-a20-display-engine
247 * allwinner,sun8i-a33-display-engine 257 * allwinner,sun8i-a33-display-engine
258 * allwinner,sun8i-a83t-display-engine
248 * allwinner,sun8i-v3s-display-engine 259 * allwinner,sun8i-v3s-display-engine
249 260
250 - allwinner,pipelines: list of phandle to the display engine 261 - allwinner,pipelines: list of phandle to the display engine
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 41cb1ff07150..159dc5c075c2 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -173,6 +173,7 @@ itead ITEAD Intelligent Systems Co.Ltd
173iwave iWave Systems Technologies Pvt. Ltd. 173iwave iWave Systems Technologies Pvt. Ltd.
174jdi Japan Display Inc. 174jdi Japan Display Inc.
175jedec JEDEC Solid State Technology Association 175jedec JEDEC Solid State Technology Association
176jianda Jiandangjing Technology Co., Ltd.
176karo Ka-Ro electronics GmbH 177karo Ka-Ro electronics GmbH
177keithkoep Keith & Koep GmbH 178keithkoep Keith & Koep GmbH
178keymile Keymile GmbH 179keymile Keymile GmbH
@@ -380,6 +381,7 @@ virtio Virtual I/O Device Specification, developed by the OASIS consortium
380vivante Vivante Corporation 381vivante Vivante Corporation
381vocore VoCore Studio 382vocore VoCore Studio
382voipac Voipac Technologies s.r.o. 383voipac Voipac Technologies s.r.o.
384vot Vision Optical Technology Co., Ltd.
383wd Western Digital Corp. 385wd Western Digital Corp.
384wetek WeTek Electronics, limited. 386wetek WeTek Electronics, limited.
385wexler Wexler 387wexler Wexler
diff --git a/MAINTAINERS b/MAINTAINERS
index d4b1635ba1f3..40aea858c7ea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4554,6 +4554,12 @@ S: Maintained
4554F: drivers/gpu/drm/tinydrm/st7586.c 4554F: drivers/gpu/drm/tinydrm/st7586.c
4555F: Documentation/devicetree/bindings/display/st7586.txt 4555F: Documentation/devicetree/bindings/display/st7586.txt
4556 4556
4557DRM DRIVER FOR SITRONIX ST7735R PANELS
4558M: David Lechner <david@lechnology.com>
4559S: Maintained
4560F: drivers/gpu/drm/tinydrm/st7735r.c
4561F: Documentation/devicetree/bindings/display/st7735r.txt
4562
4557DRM DRIVER FOR TDFX VIDEO CARDS 4563DRM DRIVER FOR TDFX VIDEO CARDS
4558S: Orphan / Obsolete 4564S: Orphan / Obsolete
4559F: drivers/gpu/drm/tdfx/ 4565F: drivers/gpu/drm/tdfx/
diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c
index 901a4e9a87a3..1f2af707ce03 100644
--- a/drivers/gpu/drm/drm_panel_orientation_quirks.c
+++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c
@@ -9,6 +9,7 @@
9 */ 9 */
10 10
11#include <linux/dmi.h> 11#include <linux/dmi.h>
12#include <linux/module.h>
12#include <drm/drm_connector.h> 13#include <drm/drm_connector.h>
13 14
14#ifdef CONFIG_DMI 15#ifdef CONFIG_DMI
@@ -172,3 +173,5 @@ int drm_get_panel_orientation_quirk(int width, int height)
172EXPORT_SYMBOL(drm_get_panel_orientation_quirk); 173EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
173 174
174#endif 175#endif
176
177MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 726f3fb3312d..6ba4031f3919 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -28,6 +28,14 @@ config DRM_PANEL_SIMPLE
28 that it can be automatically turned off when the panel goes into a 28 that it can be automatically turned off when the panel goes into a
29 low power state. 29 low power state.
30 30
31config DRM_PANEL_ILITEK_IL9322
32 tristate "Ilitek ILI9322 320x240 QVGA panels"
33 depends on OF && SPI
34 select REGMAP
35 help
36 Say Y here if you want to enable support for Ilitek IL9322
37 QVGA (320x240) RGB, YUV and ITU-T BT.656 panels.
38
31config DRM_PANEL_INNOLUX_P079ZCA 39config DRM_PANEL_INNOLUX_P079ZCA
32 tristate "Innolux P079ZCA panel" 40 tristate "Innolux P079ZCA panel"
33 depends on OF 41 depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 2c4e1a93e05f..6d251ebc568c 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1,6 +1,7 @@
1# SPDX-License-Identifier: GPL-2.0 1# SPDX-License-Identifier: GPL-2.0
2obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o 2obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
3obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o 3obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
4obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
4obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o 5obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
5obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o 6obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
6obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o 7obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
new file mode 100644
index 000000000000..b4ec0ecff807
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
@@ -0,0 +1,962 @@
1/*
2 * Ilitek ILI9322 TFT LCD drm_panel driver.
3 *
4 * This panel can be configured to support:
5 * - 8-bit serial RGB interface
6 * - 24-bit parallel RGB interface
7 * - 8-bit ITU-R BT.601 interface
8 * - 8-bit ITU-R BT.656 interface
9 * - Up to 320RGBx240 dots resolution TFT LCD displays
10 * - Scaling, brightness and contrast
11 *
12 * The scaling means that the display accepts a 640x480 or 720x480
13 * input and rescales it to fit to the 320x240 display. So what we
14 * present to the system is something else than what comes out on the
15 * actual display.
16 *
17 * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
18 * Derived from drivers/drm/gpu/panel/panel-samsung-ld9040.c
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License version 2 as
22 * published by the Free Software Foundation.
23 */
24
25#include <drm/drmP.h>
26#include <drm/drm_panel.h>
27
28#include <linux/of_device.h>
29#include <linux/bitops.h>
30#include <linux/gpio/consumer.h>
31#include <linux/module.h>
32#include <linux/regmap.h>
33#include <linux/regulator/consumer.h>
34#include <linux/spi/spi.h>
35
36#include <video/mipi_display.h>
37#include <video/of_videomode.h>
38#include <video/videomode.h>
39
40#define ILI9322_CHIP_ID 0x00
41#define ILI9322_CHIP_ID_MAGIC 0x96
42
43/*
44 * Voltage on the communication interface, from 0.7 (0x00)
45 * to 1.32 (0x1f) times the VREG1OUT voltage in 2% increments.
46 * 1.00 (0x0f) is the default.
47 */
48#define ILI9322_VCOM_AMP 0x01
49
50/*
51 * High voltage on the communication signals, from 0.37 (0x00) to
52 * 1.0 (0x3f) times the VREGOUT1 voltage in 1% increments.
53 * 0.83 (0x2e) is the default.
54 */
55#define ILI9322_VCOM_HIGH 0x02
56
57/*
58 * VREG1 voltage regulator from 3.6V (0x00) to 6.0V (0x18) in 0.1V
59 * increments. 5.4V (0x12) is the default. This is the reference
60 * voltage for the VCOM levels and the greyscale level.
61 */
62#define ILI9322_VREG1_VOLTAGE 0x03
63
64/* Describes the incoming signal */
65#define ILI9322_ENTRY 0x06
66/* 0 = right-to-left, 1 = left-to-right (default), horizontal flip */
67#define ILI9322_ENTRY_HDIR BIT(0)
68/* 0 = down-to-up, 1 = up-to-down (default), vertical flip */
69#define ILI9322_ENTRY_VDIR BIT(1)
70/* NTSC, PAL or autodetect */
71#define ILI9322_ENTRY_NTSC (0 << 2)
72#define ILI9322_ENTRY_PAL (1 << 2)
73#define ILI9322_ENTRY_AUTODETECT (3 << 2)
74/* Input format */
75#define ILI9322_ENTRY_SERIAL_RGB_THROUGH (0 << 4)
76#define ILI9322_ENTRY_SERIAL_RGB_ALIGNED (1 << 4)
77#define ILI9322_ENTRY_SERIAL_RGB_DUMMY_320X240 (2 << 4)
78#define ILI9322_ENTRY_SERIAL_RGB_DUMMY_360X240 (3 << 4)
79#define ILI9322_ENTRY_DISABLE_1 (4 << 4)
80#define ILI9322_ENTRY_PARALLEL_RGB_THROUGH (5 << 4)
81#define ILI9322_ENTRY_PARALLEL_RGB_ALIGNED (6 << 4)
82#define ILI9322_ENTRY_YUV_640Y_320CBCR_25_54_MHZ (7 << 4)
83#define ILI9322_ENTRY_YUV_720Y_360CBCR_27_MHZ (8 << 4)
84#define ILI9322_ENTRY_DISABLE_2 (9 << 4)
85#define ILI9322_ENTRY_ITU_R_BT_656_720X360 (10 << 4)
86#define ILI9322_ENTRY_ITU_R_BT_656_640X320 (11 << 4)
87
88/* Power control */
89#define ILI9322_POW_CTRL 0x07
90#define ILI9322_POW_CTRL_STB BIT(0) /* 0 = standby, 1 = normal */
91#define ILI9322_POW_CTRL_VGL BIT(1) /* 0 = off, 1 = on */
92#define ILI9322_POW_CTRL_VGH BIT(2) /* 0 = off, 1 = on */
93#define ILI9322_POW_CTRL_DDVDH BIT(3) /* 0 = off, 1 = on */
94#define ILI9322_POW_CTRL_VCOM BIT(4) /* 0 = off, 1 = on */
95#define ILI9322_POW_CTRL_VCL BIT(5) /* 0 = off, 1 = on */
96#define ILI9322_POW_CTRL_AUTO BIT(6) /* 0 = interactive, 1 = auto */
97#define ILI9322_POW_CTRL_STANDBY (ILI9322_POW_CTRL_VGL | \
98 ILI9322_POW_CTRL_VGH | \
99 ILI9322_POW_CTRL_DDVDH | \
100 ILI9322_POW_CTRL_VCL | \
101 ILI9322_POW_CTRL_AUTO | \
102 BIT(7))
103#define ILI9322_POW_CTRL_DEFAULT (ILI9322_POW_CTRL_STANDBY | \
104 ILI9322_POW_CTRL_STB)
105
106/* Vertical back porch bits 0..5 */
107#define ILI9322_VBP 0x08
108
109/* Horizontal back porch, 8 bits */
110#define ILI9322_HBP 0x09
111
112/*
113 * Polarity settings:
114 * 1 = positive polarity
115 * 0 = negative polarity
116 */
117#define ILI9322_POL 0x0a
118#define ILI9322_POL_DCLK BIT(0) /* 1 default */
119#define ILI9322_POL_HSYNC BIT(1) /* 0 default */
120#define ILI9322_POL_VSYNC BIT(2) /* 0 default */
121#define ILI9322_POL_DE BIT(3) /* 1 default */
122/*
123 * 0 means YCBCR are ordered Cb0,Y0,Cr0,Y1,Cb2,Y2,Cr2,Y3 (default)
124 * in RGB mode this means RGB comes in RGBRGB
125 * 1 means YCBCR are ordered Cr0,Y0,Cb0,Y1,Cr2,Y2,Cb2,Y3
126 * in RGB mode this means RGB comes in BGRBGR
127 */
128#define ILI9322_POL_YCBCR_MODE BIT(4)
129/* Formula A for YCbCR->RGB = 0, Formula B = 1 */
130#define ILI9322_POL_FORMULA BIT(5)
131/* Reverse polarity: 0 = 0..255, 1 = 255..0 */
132#define ILI9322_POL_REV BIT(6)
133
134#define ILI9322_IF_CTRL 0x0b
135#define ILI9322_IF_CTRL_HSYNC_VSYNC 0x00
136#define ILI9322_IF_CTRL_HSYNC_VSYNC_DE BIT(2)
137#define ILI9322_IF_CTRL_DE_ONLY BIT(3)
138#define ILI9322_IF_CTRL_SYNC_DISABLED (BIT(2) | BIT(3))
139#define ILI9322_IF_CTRL_LINE_INVERSION BIT(0) /* Not set means frame inv */
140
141#define ILI9322_GLOBAL_RESET 0x04
142#define ILI9322_GLOBAL_RESET_ASSERT 0x00 /* bit 0 = 0 -> reset */
143
144/*
145 * 4+4 bits of negative and positive gamma correction
146 * Upper nybble, bits 4-7 are negative gamma
147 * Lower nybble, bits 0-3 are positive gamma
148 */
149#define ILI9322_GAMMA_1 0x10
150#define ILI9322_GAMMA_2 0x11
151#define ILI9322_GAMMA_3 0x12
152#define ILI9322_GAMMA_4 0x13
153#define ILI9322_GAMMA_5 0x14
154#define ILI9322_GAMMA_6 0x15
155#define ILI9322_GAMMA_7 0x16
156#define ILI9322_GAMMA_8 0x17
157
158/**
159 * enum ili9322_input - the format of the incoming signal to the panel
160 *
161 * The panel can be connected to various input streams and four of them can
162 * be selected by electronic straps on the display. However it is possible
163 * to select another mode or override the electronic default with this
164 * setting.
165 */
166enum ili9322_input {
167 ILI9322_INPUT_SRGB_THROUGH = 0x0,
168 ILI9322_INPUT_SRGB_ALIGNED = 0x1,
169 ILI9322_INPUT_SRGB_DUMMY_320X240 = 0x2,
170 ILI9322_INPUT_SRGB_DUMMY_360X240 = 0x3,
171 ILI9322_INPUT_DISABLED_1 = 0x4,
172 ILI9322_INPUT_PRGB_THROUGH = 0x5,
173 ILI9322_INPUT_PRGB_ALIGNED = 0x6,
174 ILI9322_INPUT_YUV_640X320_YCBCR = 0x7,
175 ILI9322_INPUT_YUV_720X360_YCBCR = 0x8,
176 ILI9322_INPUT_DISABLED_2 = 0x9,
177 ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR = 0xa,
178 ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR = 0xb,
179 ILI9322_INPUT_UNKNOWN = 0xc,
180};
181
182const char *ili9322_inputs[] = {
183 "8 bit serial RGB through",
184 "8 bit serial RGB aligned",
185 "8 bit serial RGB dummy 320x240",
186 "8 bit serial RGB dummy 360x240",
187 "disabled 1",
188 "24 bit parallel RGB through",
189 "24 bit parallel RGB aligned",
190 "24 bit YUV 640Y 320CbCr",
191 "24 bit YUV 720Y 360CbCr",
192 "disabled 2",
193 "8 bit ITU-R BT.656 720Y 360CbCr",
194 "8 bit ITU-R BT.656 640Y 320CbCr",
195};
196
197/**
198 * struct ili9322_config - the system specific ILI9322 configuration
199 * @width_mm: physical panel width [mm]
200 * @height_mm: physical panel height [mm]
201 * @flip_horizontal: flip the image horizontally (right-to-left scan)
202 * (only in RGB and YUV modes)
203 * @flip_vertical: flip the image vertically (down-to-up scan)
204 * (only in RGB and YUV modes)
205 * @input: the input/entry type used in this system, if this is set to
206 * ILI9322_INPUT_UNKNOWN the driver will try to figure it out by probing
207 * the hardware
208 * @vreg1out_mv: the output in microvolts for the VREGOUT1 regulator used
209 * to drive the physical display. Valid ranges are 3600 thru 6000 in 100
210 * microvolt increments. If not specified, hardware defaults will be
211 * used (4.5V).
212 * @vcom_high_percent: the percentage of VREGOUT1 used for the peak
213 * voltage on the communications link. Valid ranges are 37 thru 100
214 * percent. If not specified, hardware defaults will be used (91%).
215 * @vcom_amplitude_percent: the percentage of VREGOUT1 used for the
216 * peak-to-peak amplitude of the communcation signals to the physical
217 * display. Valid ranges are 70 thru 132 percent in increments if two
218 * percent. Odd percentages will be truncated. If not specified, hardware
219 * defaults will be used (114%).
220 * @dclk_active_high: data/pixel clock active high, data will be clocked
221 * in on the rising edge of the DCLK (this is usually the case).
222 * @syncmode: The synchronization mode, what sync signals are emitted.
223 * See the enum for details.
224 * @de_active_high: DE (data entry) is active high
225 * @hsync_active_high: HSYNC is active high
226 * @vsync_active_high: VSYNC is active high
227 * @gamma_corr_pos: a set of 8 nybbles describing positive
228 * gamma correction for voltages V1 thru V8. Valid range 0..15
229 * @gamma_corr_neg: a set of 8 nybbles describing negative
230 * gamma correction for voltages V1 thru V8. Valid range 0..15
231 *
232 * These adjust what grayscale voltage will be output for input data V1 = 0,
233 * V2 = 16, V3 = 48, V4 = 96, V5 = 160, V6 = 208, V7 = 240 and V8 = 255.
234 * The curve is shaped like this:
235 *
236 * ^
237 * | V8
238 * | V7
239 * | V6
240 * | V5
241 * | V4
242 * | V3
243 * | V2
244 * | V1
245 * +----------------------------------------------------------->
246 * 0 16 48 96 160 208 240 255
247 *
248 * The negative and postive gamma values adjust the V1 thru V8 up/down
249 * according to the datasheet specifications. This is a property of the
250 * physical display connected to the display controller and may vary.
251 * If defined, both arrays must be supplied in full. If the properties
252 * are not supplied, hardware defaults will be used.
253 */
254struct ili9322_config {
255 u32 width_mm;
256 u32 height_mm;
257 bool flip_horizontal;
258 bool flip_vertical;
259 enum ili9322_input input;
260 u32 vreg1out_mv;
261 u32 vcom_high_percent;
262 u32 vcom_amplitude_percent;
263 bool dclk_active_high;
264 bool de_active_high;
265 bool hsync_active_high;
266 bool vsync_active_high;
267 u8 syncmode;
268 u8 gamma_corr_pos[8];
269 u8 gamma_corr_neg[8];
270};
271
272struct ili9322 {
273 struct device *dev;
274 const struct ili9322_config *conf;
275 struct drm_panel panel;
276 struct regmap *regmap;
277 struct regulator_bulk_data supplies[3];
278 struct gpio_desc *reset_gpio;
279 enum ili9322_input input;
280 struct videomode vm;
281 u8 gamma[8];
282 u8 vreg1out;
283 u8 vcom_high;
284 u8 vcom_amplitude;
285};
286
287static inline struct ili9322 *panel_to_ili9322(struct drm_panel *panel)
288{
289 return container_of(panel, struct ili9322, panel);
290}
291
292static int ili9322_regmap_spi_write(void *context, const void *data,
293 size_t count)
294{
295 struct device *dev = context;
296 struct spi_device *spi = to_spi_device(dev);
297 u8 buf[2];
298
299 /* Clear bit 7 to write */
300 memcpy(buf, data, 2);
301 buf[0] &= ~0x80;
302
303 dev_dbg(dev, "WRITE: %02x %02x\n", buf[0], buf[1]);
304 return spi_write_then_read(spi, buf, 2, NULL, 0);
305}
306
307static int ili9322_regmap_spi_read(void *context, const void *reg,
308 size_t reg_size, void *val, size_t val_size)
309{
310 struct device *dev = context;
311 struct spi_device *spi = to_spi_device(dev);
312 u8 buf[1];
313
314 /* Set bit 7 to 1 to read */
315 memcpy(buf, reg, 1);
316 dev_dbg(dev, "READ: %02x reg size = %zu, val size = %zu\n",
317 buf[0], reg_size, val_size);
318 buf[0] |= 0x80;
319
320 return spi_write_then_read(spi, buf, 1, val, 1);
321}
322
323static struct regmap_bus ili9322_regmap_bus = {
324 .write = ili9322_regmap_spi_write,
325 .read = ili9322_regmap_spi_read,
326 .reg_format_endian_default = REGMAP_ENDIAN_BIG,
327 .val_format_endian_default = REGMAP_ENDIAN_BIG,
328};
329
330static bool ili9322_volatile_reg(struct device *dev, unsigned int reg)
331{
332 return false;
333}
334
335static bool ili9322_writeable_reg(struct device *dev, unsigned int reg)
336{
337 /* Just register 0 is read-only */
338 if (reg == 0x00)
339 return false;
340 return true;
341}
342
343const struct regmap_config ili9322_regmap_config = {
344 .reg_bits = 8,
345 .val_bits = 8,
346 .max_register = 0x44,
347 .cache_type = REGCACHE_RBTREE,
348 .volatile_reg = ili9322_volatile_reg,
349 .writeable_reg = ili9322_writeable_reg,
350};
351
352static int ili9322_init(struct drm_panel *panel, struct ili9322 *ili)
353{
354 struct drm_connector *connector = panel->connector;
355 u8 reg;
356 int ret;
357 int i;
358
359 /* Reset display */
360 ret = regmap_write(ili->regmap, ILI9322_GLOBAL_RESET,
361 ILI9322_GLOBAL_RESET_ASSERT);
362 if (ret) {
363 dev_err(ili->dev, "can't issue GRESET (%d)\n", ret);
364 return ret;
365 }
366
367 /* Set up the main voltage regulator */
368 if (ili->vreg1out != U8_MAX) {
369 ret = regmap_write(ili->regmap, ILI9322_VREG1_VOLTAGE,
370 ili->vreg1out);
371 if (ret) {
372 dev_err(ili->dev, "can't set up VREG1OUT (%d)\n", ret);
373 return ret;
374 }
375 }
376
377 if (ili->vcom_amplitude != U8_MAX) {
378 ret = regmap_write(ili->regmap, ILI9322_VCOM_AMP,
379 ili->vcom_amplitude);
380 if (ret) {
381 dev_err(ili->dev,
382 "can't set up VCOM amplitude (%d)\n", ret);
383 return ret;
384 }
385 };
386
387 if (ili->vcom_high != U8_MAX) {
388 ret = regmap_write(ili->regmap, ILI9322_VCOM_HIGH,
389 ili->vcom_high);
390 if (ret) {
391 dev_err(ili->dev, "can't set up VCOM high (%d)\n", ret);
392 return ret;
393 }
394 };
395
396 /* Set up gamma correction */
397 for (i = 0; i < ARRAY_SIZE(ili->gamma); i++) {
398 ret = regmap_write(ili->regmap, ILI9322_GAMMA_1 + i,
399 ili->gamma[i]);
400 if (ret) {
401 dev_err(ili->dev,
402 "can't write gamma V%d to 0x%02x (%d)\n",
403 i + 1, ILI9322_GAMMA_1 + i, ret);
404 return ret;
405 }
406 }
407
408 /*
409 * Polarity and inverted color order for RGB input.
410 * None of this applies in the BT.656 mode.
411 */
412 if (ili->conf->dclk_active_high) {
413 reg = ILI9322_POL_DCLK;
414 connector->display_info.bus_flags |=
415 DRM_BUS_FLAG_PIXDATA_POSEDGE;
416 } else {
417 reg = 0;
418 connector->display_info.bus_flags |=
419 DRM_BUS_FLAG_PIXDATA_NEGEDGE;
420 }
421 if (ili->conf->de_active_high) {
422 reg |= ILI9322_POL_DE;
423 connector->display_info.bus_flags |=
424 DRM_BUS_FLAG_DE_HIGH;
425 } else {
426 connector->display_info.bus_flags |=
427 DRM_BUS_FLAG_DE_LOW;
428 }
429 if (ili->conf->hsync_active_high)
430 reg |= ILI9322_POL_HSYNC;
431 if (ili->conf->vsync_active_high)
432 reg |= ILI9322_POL_VSYNC;
433 ret = regmap_write(ili->regmap, ILI9322_POL, reg);
434 if (ret) {
435 dev_err(ili->dev, "can't write POL register (%d)\n", ret);
436 return ret;
437 }
438
439 /*
440 * Set up interface control.
441 * This is not used in the BT.656 mode (no H/Vsync or DE signals).
442 */
443 reg = ili->conf->syncmode;
444 reg |= ILI9322_IF_CTRL_LINE_INVERSION;
445 ret = regmap_write(ili->regmap, ILI9322_IF_CTRL, reg);
446 if (ret) {
447 dev_err(ili->dev, "can't write IF CTRL register (%d)\n", ret);
448 return ret;
449 }
450
451 /* Set up the input mode */
452 reg = (ili->input << 4);
453 /* These are inverted, setting to 1 is the default, clearing flips */
454 if (!ili->conf->flip_horizontal)
455 reg |= ILI9322_ENTRY_HDIR;
456 if (!ili->conf->flip_vertical)
457 reg |= ILI9322_ENTRY_VDIR;
458 reg |= ILI9322_ENTRY_AUTODETECT;
459 ret = regmap_write(ili->regmap, ILI9322_ENTRY, reg);
460 if (ret) {
461 dev_err(ili->dev, "can't write ENTRY reg (%d)\n", ret);
462 return ret;
463 }
464 dev_info(ili->dev, "display is in %s mode, syncmode %02x\n",
465 ili9322_inputs[ili->input],
466 ili->conf->syncmode);
467
468 dev_info(ili->dev, "initialized display\n");
469
470 return 0;
471}
472
473/*
474 * This power-on sequence if from the datasheet, page 57.
475 */
476static int ili9322_power_on(struct ili9322 *ili)
477{
478 int ret;
479
480 /* Assert RESET */
481 gpiod_set_value(ili->reset_gpio, 1);
482
483 ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies), ili->supplies);
484 if (ret < 0) {
485 dev_err(ili->dev, "unable to enable regulators\n");
486 return ret;
487 }
488 msleep(20);
489
490 /* De-assert RESET */
491 gpiod_set_value(ili->reset_gpio, 0);
492
493 msleep(10);
494
495 return 0;
496}
497
498static int ili9322_power_off(struct ili9322 *ili)
499{
500 return regulator_bulk_disable(ARRAY_SIZE(ili->supplies), ili->supplies);
501}
502
503static int ili9322_disable(struct drm_panel *panel)
504{
505 struct ili9322 *ili = panel_to_ili9322(panel);
506 int ret;
507
508 ret = regmap_write(ili->regmap, ILI9322_POW_CTRL,
509 ILI9322_POW_CTRL_STANDBY);
510 if (ret) {
511 dev_err(ili->dev, "unable to go to standby mode\n");
512 return ret;
513 }
514
515 return 0;
516}
517
518static int ili9322_unprepare(struct drm_panel *panel)
519{
520 struct ili9322 *ili = panel_to_ili9322(panel);
521
522 return ili9322_power_off(ili);
523}
524
525static int ili9322_prepare(struct drm_panel *panel)
526{
527 struct ili9322 *ili = panel_to_ili9322(panel);
528 int ret;
529
530 ret = ili9322_power_on(ili);
531 if (ret < 0)
532 return ret;
533
534 ret = ili9322_init(panel, ili);
535 if (ret < 0)
536 ili9322_unprepare(panel);
537
538 return ret;
539}
540
541static int ili9322_enable(struct drm_panel *panel)
542{
543 struct ili9322 *ili = panel_to_ili9322(panel);
544 int ret;
545
546 ret = regmap_write(ili->regmap, ILI9322_POW_CTRL,
547 ILI9322_POW_CTRL_DEFAULT);
548 if (ret) {
549 dev_err(ili->dev, "unable to enable panel\n");
550 return ret;
551 }
552
553 return 0;
554}
555
556/* Serial RGB modes */
557static const struct drm_display_mode srgb_320x240_mode = {
558 .clock = 2453500,
559 .hdisplay = 320,
560 .hsync_start = 320 + 359,
561 .hsync_end = 320 + 359 + 1,
562 .htotal = 320 + 359 + 1 + 241,
563 .vdisplay = 240,
564 .vsync_start = 240 + 4,
565 .vsync_end = 240 + 4 + 1,
566 .vtotal = 262,
567 .vrefresh = 60,
568 .flags = 0,
569};
570
571static const struct drm_display_mode srgb_360x240_mode = {
572 .clock = 2700000,
573 .hdisplay = 360,
574 .hsync_start = 360 + 35,
575 .hsync_end = 360 + 35 + 1,
576 .htotal = 360 + 35 + 1 + 241,
577 .vdisplay = 240,
578 .vsync_start = 240 + 21,
579 .vsync_end = 240 + 21 + 1,
580 .vtotal = 262,
581 .vrefresh = 60,
582 .flags = 0,
583};
584
585/* This is the only mode listed for parallel RGB in the datasheet */
586static const struct drm_display_mode prgb_320x240_mode = {
587 .clock = 6400000,
588 .hdisplay = 320,
589 .hsync_start = 320 + 38,
590 .hsync_end = 320 + 38 + 1,
591 .htotal = 320 + 38 + 1 + 50,
592 .vdisplay = 240,
593 .vsync_start = 240 + 4,
594 .vsync_end = 240 + 4 + 1,
595 .vtotal = 262,
596 .vrefresh = 60,
597 .flags = 0,
598};
599
600/* YUV modes */
601static const struct drm_display_mode yuv_640x320_mode = {
602 .clock = 2454000,
603 .hdisplay = 640,
604 .hsync_start = 640 + 252,
605 .hsync_end = 640 + 252 + 1,
606 .htotal = 640 + 252 + 1 + 28,
607 .vdisplay = 320,
608 .vsync_start = 320 + 4,
609 .vsync_end = 320 + 4 + 1,
610 .vtotal = 320 + 4 + 1 + 18,
611 .vrefresh = 60,
612 .flags = 0,
613};
614
615static const struct drm_display_mode yuv_720x360_mode = {
616 .clock = 2700000,
617 .hdisplay = 720,
618 .hsync_start = 720 + 252,
619 .hsync_end = 720 + 252 + 1,
620 .htotal = 720 + 252 + 1 + 24,
621 .vdisplay = 360,
622 .vsync_start = 360 + 4,
623 .vsync_end = 360 + 4 + 1,
624 .vtotal = 360 + 4 + 1 + 18,
625 .vrefresh = 60,
626 .flags = 0,
627};
628
629/* BT.656 VGA mode, 640x480 */
630static const struct drm_display_mode itu_r_bt_656_640_mode = {
631 .clock = 2454000,
632 .hdisplay = 640,
633 .hsync_start = 640 + 3,
634 .hsync_end = 640 + 3 + 1,
635 .htotal = 640 + 3 + 1 + 272,
636 .vdisplay = 480,
637 .vsync_start = 480 + 4,
638 .vsync_end = 480 + 4 + 1,
639 .vtotal = 500,
640 .vrefresh = 60,
641 .flags = 0,
642};
643
644/* BT.656 D1 mode 720x480 */
645static const struct drm_display_mode itu_r_bt_656_720_mode = {
646 .clock = 2700000,
647 .hdisplay = 720,
648 .hsync_start = 720 + 3,
649 .hsync_end = 720 + 3 + 1,
650 .htotal = 720 + 3 + 1 + 272,
651 .vdisplay = 480,
652 .vsync_start = 480 + 4,
653 .vsync_end = 480 + 4 + 1,
654 .vtotal = 500,
655 .vrefresh = 60,
656 .flags = 0,
657};
658
659static int ili9322_get_modes(struct drm_panel *panel)
660{
661 struct drm_connector *connector = panel->connector;
662 struct ili9322 *ili = panel_to_ili9322(panel);
663 struct drm_display_mode *mode;
664
665 strncpy(connector->display_info.name, "ILI9322 TFT LCD driver\0",
666 DRM_DISPLAY_INFO_LEN);
667 connector->display_info.width_mm = ili->conf->width_mm;
668 connector->display_info.height_mm = ili->conf->height_mm;
669
670 switch (ili->input) {
671 case ILI9322_INPUT_SRGB_DUMMY_320X240:
672 mode = drm_mode_duplicate(panel->drm, &srgb_320x240_mode);
673 break;
674 case ILI9322_INPUT_SRGB_DUMMY_360X240:
675 mode = drm_mode_duplicate(panel->drm, &srgb_360x240_mode);
676 break;
677 case ILI9322_INPUT_PRGB_THROUGH:
678 case ILI9322_INPUT_PRGB_ALIGNED:
679 mode = drm_mode_duplicate(panel->drm, &prgb_320x240_mode);
680 break;
681 case ILI9322_INPUT_YUV_640X320_YCBCR:
682 mode = drm_mode_duplicate(panel->drm, &yuv_640x320_mode);
683 break;
684 case ILI9322_INPUT_YUV_720X360_YCBCR:
685 mode = drm_mode_duplicate(panel->drm, &yuv_720x360_mode);
686 break;
687 case ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR:
688 mode = drm_mode_duplicate(panel->drm, &itu_r_bt_656_720_mode);
689 break;
690 case ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR:
691 mode = drm_mode_duplicate(panel->drm, &itu_r_bt_656_640_mode);
692 break;
693 default:
694 mode = NULL;
695 break;
696 }
697 if (!mode) {
698 DRM_ERROR("bad mode or failed to add mode\n");
699 return -EINVAL;
700 }
701 drm_mode_set_name(mode);
702 /*
703 * This is the preferred mode because most people are going
704 * to want to use the display with VGA type graphics.
705 */
706 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
707
708 /* Set up the polarity */
709 if (ili->conf->hsync_active_high)
710 mode->flags |= DRM_MODE_FLAG_PHSYNC;
711 else
712 mode->flags |= DRM_MODE_FLAG_NHSYNC;
713 if (ili->conf->vsync_active_high)
714 mode->flags |= DRM_MODE_FLAG_PVSYNC;
715 else
716 mode->flags |= DRM_MODE_FLAG_NVSYNC;
717
718 mode->width_mm = ili->conf->width_mm;
719 mode->height_mm = ili->conf->height_mm;
720 drm_mode_probed_add(connector, mode);
721
722 return 1; /* Number of modes */
723}
724
725static const struct drm_panel_funcs ili9322_drm_funcs = {
726 .disable = ili9322_disable,
727 .unprepare = ili9322_unprepare,
728 .prepare = ili9322_prepare,
729 .enable = ili9322_enable,
730 .get_modes = ili9322_get_modes,
731};
732
733static int ili9322_probe(struct spi_device *spi)
734{
735 struct device *dev = &spi->dev;
736 struct ili9322 *ili;
737 const struct regmap_config *regmap_config;
738 u8 gamma;
739 u32 val;
740 int ret;
741 int i;
742
743 ili = devm_kzalloc(dev, sizeof(struct ili9322), GFP_KERNEL);
744 if (!ili)
745 return -ENOMEM;
746
747 spi_set_drvdata(spi, ili);
748
749 ili->dev = dev;
750
751 /*
752 * Every new incarnation of this display must have a unique
753 * data entry for the system in this driver.
754 */
755 ili->conf = of_device_get_match_data(dev);
756 if (!ili->conf) {
757 dev_err(dev, "missing device configuration\n");
758 return -ENODEV;
759 }
760
761 val = ili->conf->vreg1out_mv;
762 if (!val) {
763 /* Default HW value, do not touch (should be 4.5V) */
764 ili->vreg1out = U8_MAX;
765 } else {
766 if (val < 3600) {
767 dev_err(dev, "too low VREG1OUT\n");
768 return -EINVAL;
769 }
770 if (val > 6000) {
771 dev_err(dev, "too high VREG1OUT\n");
772 return -EINVAL;
773 }
774 if ((val % 100) != 0) {
775 dev_err(dev, "VREG1OUT is no even 100 microvolt\n");
776 return -EINVAL;
777 }
778 val -= 3600;
779 val /= 100;
780 dev_dbg(dev, "VREG1OUT = 0x%02x\n", val);
781 ili->vreg1out = val;
782 }
783
784 val = ili->conf->vcom_high_percent;
785 if (!val) {
786 /* Default HW value, do not touch (should be 91%) */
787 ili->vcom_high = U8_MAX;
788 } else {
789 if (val < 37) {
790 dev_err(dev, "too low VCOM high\n");
791 return -EINVAL;
792 }
793 if (val > 100) {
794 dev_err(dev, "too high VCOM high\n");
795 return -EINVAL;
796 }
797 val -= 37;
798 dev_dbg(dev, "VCOM high = 0x%02x\n", val);
799 ili->vcom_high = val;
800 }
801
802 val = ili->conf->vcom_amplitude_percent;
803 if (!val) {
804 /* Default HW value, do not touch (should be 114%) */
805 ili->vcom_high = U8_MAX;
806 } else {
807 if (val < 70) {
808 dev_err(dev, "too low VCOM amplitude\n");
809 return -EINVAL;
810 }
811 if (val > 132) {
812 dev_err(dev, "too high VCOM amplitude\n");
813 return -EINVAL;
814 }
815 val -= 70;
816 val >>= 1; /* Increments of 2% */
817 dev_dbg(dev, "VCOM amplitude = 0x%02x\n", val);
818 ili->vcom_amplitude = val;
819 }
820
821 for (i = 0; i < ARRAY_SIZE(ili->gamma); i++) {
822 val = ili->conf->gamma_corr_neg[i];
823 if (val > 15) {
824 dev_err(dev, "negative gamma %u > 15, capping\n", val);
825 val = 15;
826 }
827 gamma = val << 4;
828 val = ili->conf->gamma_corr_pos[i];
829 if (val > 15) {
830 dev_err(dev, "positive gamma %u > 15, capping\n", val);
831 val = 15;
832 }
833 gamma |= val;
834 ili->gamma[i] = gamma;
835 dev_dbg(dev, "gamma V%d: 0x%02x\n", i + 1, gamma);
836 }
837
838 ili->supplies[0].supply = "vcc"; /* 2.7-3.6 V */
839 ili->supplies[1].supply = "iovcc"; /* 1.65-3.6V */
840 ili->supplies[2].supply = "vci"; /* 2.7-3.6V */
841 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies),
842 ili->supplies);
843 if (ret < 0)
844 return ret;
845 ret = regulator_set_voltage(ili->supplies[0].consumer,
846 2700000, 3600000);
847 if (ret)
848 return ret;
849 ret = regulator_set_voltage(ili->supplies[1].consumer,
850 1650000, 3600000);
851 if (ret)
852 return ret;
853 ret = regulator_set_voltage(ili->supplies[2].consumer,
854 2700000, 3600000);
855 if (ret)
856 return ret;
857
858 ili->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
859 if (IS_ERR(ili->reset_gpio)) {
860 dev_err(dev, "failed to get RESET GPIO\n");
861 return PTR_ERR(ili->reset_gpio);
862 }
863
864 spi->bits_per_word = 8;
865 ret = spi_setup(spi);
866 if (ret < 0) {
867 dev_err(dev, "spi setup failed.\n");
868 return ret;
869 }
870 regmap_config = &ili9322_regmap_config;
871 ili->regmap = devm_regmap_init(dev, &ili9322_regmap_bus, dev,
872 regmap_config);
873 if (IS_ERR(ili->regmap)) {
874 dev_err(dev, "failed to allocate register map\n");
875 return PTR_ERR(ili->regmap);
876 }
877
878 ret = regmap_read(ili->regmap, ILI9322_CHIP_ID, &val);
879 if (ret) {
880 dev_err(dev, "can't get chip ID (%d)\n", ret);
881 return ret;
882 }
883 if (val != ILI9322_CHIP_ID_MAGIC) {
884 dev_err(dev, "chip ID 0x%0x2, expected 0x%02x\n", val,
885 ILI9322_CHIP_ID_MAGIC);
886 return -ENODEV;
887 }
888
889 /* Probe the system to find the display setting */
890 if (ili->conf->input == ILI9322_INPUT_UNKNOWN) {
891 ret = regmap_read(ili->regmap, ILI9322_ENTRY, &val);
892 if (ret) {
893 dev_err(dev, "can't get entry setting (%d)\n", ret);
894 return ret;
895 }
896 /* Input enum corresponds to HW setting */
897 ili->input = (val >> 4) & 0x0f;
898 if (ili->input >= ILI9322_INPUT_UNKNOWN)
899 ili->input = ILI9322_INPUT_UNKNOWN;
900 } else {
901 ili->input = ili->conf->input;
902 }
903
904 drm_panel_init(&ili->panel);
905 ili->panel.dev = dev;
906 ili->panel.funcs = &ili9322_drm_funcs;
907
908 return drm_panel_add(&ili->panel);
909}
910
911static int ili9322_remove(struct spi_device *spi)
912{
913 struct ili9322 *ili = spi_get_drvdata(spi);
914
915 ili9322_power_off(ili);
916 drm_panel_remove(&ili->panel);
917
918 return 0;
919}
920
921/*
922 * The D-Link DIR-685 panel is marked LM918A01-1A SY-B4-091116-E0199
923 */
924static const struct ili9322_config ili9322_dir_685 = {
925 .width_mm = 65,
926 .height_mm = 50,
927 .input = ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR,
928 .vreg1out_mv = 4600,
929 .vcom_high_percent = 91,
930 .vcom_amplitude_percent = 114,
931 .syncmode = ILI9322_IF_CTRL_SYNC_DISABLED,
932 .dclk_active_high = true,
933 .gamma_corr_neg = { 0xa, 0x5, 0x7, 0x7, 0x7, 0x5, 0x1, 0x6 },
934 .gamma_corr_pos = { 0x7, 0x7, 0x3, 0x2, 0x3, 0x5, 0x7, 0x2 },
935};
936
937static const struct of_device_id ili9322_of_match[] = {
938 {
939 .compatible = "dlink,dir-685-panel",
940 .data = &ili9322_dir_685,
941 },
942 {
943 .compatible = "ilitek,ili9322",
944 .data = NULL,
945 },
946 { }
947};
948MODULE_DEVICE_TABLE(of, ili9322_of_match);
949
950static struct spi_driver ili9322_driver = {
951 .probe = ili9322_probe,
952 .remove = ili9322_remove,
953 .driver = {
954 .name = "panel-ilitek-ili9322",
955 .of_match_table = ili9322_of_match,
956 },
957};
958module_spi_driver(ili9322_driver);
959
960MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
961MODULE_DESCRIPTION("ILI9322 LCD panel driver");
962MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c
index e2d57c01200b..57e38a9e7ab4 100644
--- a/drivers/gpu/drm/panel/panel-lvds.c
+++ b/drivers/gpu/drm/panel/panel-lvds.c
@@ -17,6 +17,7 @@
17#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/of_platform.h> 18#include <linux/of_platform.h>
19#include <linux/platform_device.h> 19#include <linux/platform_device.h>
20#include <linux/regulator/consumer.h>
20#include <linux/slab.h> 21#include <linux/slab.h>
21 22
22#include <drm/drmP.h> 23#include <drm/drmP.h>
@@ -39,6 +40,7 @@ struct panel_lvds {
39 bool data_mirror; 40 bool data_mirror;
40 41
41 struct backlight_device *backlight; 42 struct backlight_device *backlight;
43 struct regulator *supply;
42 44
43 struct gpio_desc *enable_gpio; 45 struct gpio_desc *enable_gpio;
44 struct gpio_desc *reset_gpio; 46 struct gpio_desc *reset_gpio;
@@ -69,6 +71,9 @@ static int panel_lvds_unprepare(struct drm_panel *panel)
69 if (lvds->enable_gpio) 71 if (lvds->enable_gpio)
70 gpiod_set_value_cansleep(lvds->enable_gpio, 0); 72 gpiod_set_value_cansleep(lvds->enable_gpio, 0);
71 73
74 if (lvds->supply)
75 regulator_disable(lvds->supply);
76
72 return 0; 77 return 0;
73} 78}
74 79
@@ -76,6 +81,17 @@ static int panel_lvds_prepare(struct drm_panel *panel)
76{ 81{
77 struct panel_lvds *lvds = to_panel_lvds(panel); 82 struct panel_lvds *lvds = to_panel_lvds(panel);
78 83
84 if (lvds->supply) {
85 int err;
86
87 err = regulator_enable(lvds->supply);
88 if (err < 0) {
89 dev_err(lvds->dev, "failed to enable supply: %d\n",
90 err);
91 return err;
92 }
93 }
94
79 if (lvds->enable_gpio) 95 if (lvds->enable_gpio)
80 gpiod_set_value_cansleep(lvds->enable_gpio, 1); 96 gpiod_set_value_cansleep(lvds->enable_gpio, 1);
81 97
@@ -196,6 +212,13 @@ static int panel_lvds_probe(struct platform_device *pdev)
196 if (ret < 0) 212 if (ret < 0)
197 return ret; 213 return ret;
198 214
215 lvds->supply = devm_regulator_get_optional(lvds->dev, "power");
216 if (IS_ERR(lvds->supply)) {
217 ret = PTR_ERR(lvds->supply);
218 dev_err(lvds->dev, "failed to request regulator: %d\n", ret);
219 return ret;
220 }
221
199 /* Get GPIOs and backlight controller. */ 222 /* Get GPIOs and backlight controller. */
200 lvds->enable_gpio = devm_gpiod_get_optional(lvds->dev, "enable", 223 lvds->enable_gpio = devm_gpiod_get_optional(lvds->dev, "enable",
201 GPIOD_OUT_LOW); 224 GPIOD_OUT_LOW);
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 82a6ac57fbe3..2b37a6abbb1d 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -15,6 +15,7 @@ sun8i-mixer-y += sun8i_mixer.o sun8i_ui_layer.o \
15 15
16sun4i-tcon-y += sun4i_crtc.o 16sun4i-tcon-y += sun4i_crtc.o
17sun4i-tcon-y += sun4i_dotclock.o 17sun4i-tcon-y += sun4i_dotclock.o
18sun4i-tcon-y += sun4i_lvds.o
18sun4i-tcon-y += sun4i_tcon.o 19sun4i-tcon-y += sun4i_tcon.o
19sun4i-tcon-y += sun4i_rgb.o 20sun4i-tcon-y += sun4i_rgb.o
20 21
diff --git a/drivers/gpu/drm/sun4i/sun4i_dotclock.c b/drivers/gpu/drm/sun4i/sun4i_dotclock.c
index d401156490f3..023f39bda633 100644
--- a/drivers/gpu/drm/sun4i/sun4i_dotclock.c
+++ b/drivers/gpu/drm/sun4i/sun4i_dotclock.c
@@ -17,8 +17,9 @@
17#include "sun4i_dotclock.h" 17#include "sun4i_dotclock.h"
18 18
19struct sun4i_dclk { 19struct sun4i_dclk {
20 struct clk_hw hw; 20 struct clk_hw hw;
21 struct regmap *regmap; 21 struct regmap *regmap;
22 struct sun4i_tcon *tcon;
22}; 23};
23 24
24static inline struct sun4i_dclk *hw_to_dclk(struct clk_hw *hw) 25static inline struct sun4i_dclk *hw_to_dclk(struct clk_hw *hw)
@@ -73,11 +74,13 @@ static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
73static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, 74static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
74 unsigned long *parent_rate) 75 unsigned long *parent_rate)
75{ 76{
77 struct sun4i_dclk *dclk = hw_to_dclk(hw);
78 struct sun4i_tcon *tcon = dclk->tcon;
76 unsigned long best_parent = 0; 79 unsigned long best_parent = 0;
77 u8 best_div = 1; 80 u8 best_div = 1;
78 int i; 81 int i;
79 82
80 for (i = 6; i <= 127; i++) { 83 for (i = tcon->dclk_min_div; i <= tcon->dclk_max_div; i++) {
81 unsigned long ideal = rate * i; 84 unsigned long ideal = rate * i;
82 unsigned long rounded; 85 unsigned long rounded;
83 86
@@ -167,6 +170,7 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
167 dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL); 170 dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL);
168 if (!dclk) 171 if (!dclk)
169 return -ENOMEM; 172 return -ENOMEM;
173 dclk->tcon = tcon;
170 174
171 init.name = clk_name; 175 init.name = clk_name;
172 init.ops = &sun4i_dclk_ops; 176 init.ops = &sun4i_dclk_ops;
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 2b4717604eb3..4570da0227b4 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -339,6 +339,7 @@ static const struct of_device_id sun4i_drv_of_table[] = {
339 { .compatible = "allwinner,sun6i-a31s-display-engine" }, 339 { .compatible = "allwinner,sun6i-a31s-display-engine" },
340 { .compatible = "allwinner,sun7i-a20-display-engine" }, 340 { .compatible = "allwinner,sun7i-a20-display-engine" },
341 { .compatible = "allwinner,sun8i-a33-display-engine" }, 341 { .compatible = "allwinner,sun8i-a33-display-engine" },
342 { .compatible = "allwinner,sun8i-a83t-display-engine" },
342 { .compatible = "allwinner,sun8i-v3s-display-engine" }, 343 { .compatible = "allwinner,sun8i-v3s-display-engine" },
343 { } 344 { }
344}; 345};
diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c
new file mode 100644
index 000000000000..be3f14d7746d
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
@@ -0,0 +1,177 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2017 Free Electrons
4 * Maxime Ripard <maxime.ripard@free-electrons.com>
5 */
6
7#include <linux/clk.h>
8
9#include <drm/drmP.h>
10#include <drm/drm_atomic_helper.h>
11#include <drm/drm_crtc_helper.h>
12#include <drm/drm_of.h>
13#include <drm/drm_panel.h>
14
15#include "sun4i_crtc.h"
16#include "sun4i_tcon.h"
17#include "sun4i_lvds.h"
18
19struct sun4i_lvds {
20 struct drm_connector connector;
21 struct drm_encoder encoder;
22
23 struct sun4i_tcon *tcon;
24};
25
26static inline struct sun4i_lvds *
27drm_connector_to_sun4i_lvds(struct drm_connector *connector)
28{
29 return container_of(connector, struct sun4i_lvds,
30 connector);
31}
32
33static inline struct sun4i_lvds *
34drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
35{
36 return container_of(encoder, struct sun4i_lvds,
37 encoder);
38}
39
40static int sun4i_lvds_get_modes(struct drm_connector *connector)
41{
42 struct sun4i_lvds *lvds =
43 drm_connector_to_sun4i_lvds(connector);
44 struct sun4i_tcon *tcon = lvds->tcon;
45
46 return drm_panel_get_modes(tcon->panel);
47}
48
49static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
50 .get_modes = sun4i_lvds_get_modes,
51};
52
53static void
54sun4i_lvds_connector_destroy(struct drm_connector *connector)
55{
56 struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
57 struct sun4i_tcon *tcon = lvds->tcon;
58
59 drm_panel_detach(tcon->panel);
60 drm_connector_cleanup(connector);
61}
62
63static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
64 .fill_modes = drm_helper_probe_single_connector_modes,
65 .destroy = sun4i_lvds_connector_destroy,
66 .reset = drm_atomic_helper_connector_reset,
67 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
68 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
69};
70
71static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
72{
73 struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
74 struct sun4i_tcon *tcon = lvds->tcon;
75
76 DRM_DEBUG_DRIVER("Enabling LVDS output\n");
77
78 if (!IS_ERR(tcon->panel)) {
79 drm_panel_prepare(tcon->panel);
80 drm_panel_enable(tcon->panel);
81 }
82}
83
84static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
85{
86 struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
87 struct sun4i_tcon *tcon = lvds->tcon;
88
89 DRM_DEBUG_DRIVER("Disabling LVDS output\n");
90
91 if (!IS_ERR(tcon->panel)) {
92 drm_panel_disable(tcon->panel);
93 drm_panel_unprepare(tcon->panel);
94 }
95}
96
97static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
98 .disable = sun4i_lvds_encoder_disable,
99 .enable = sun4i_lvds_encoder_enable,
100};
101
102static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
103 .destroy = drm_encoder_cleanup,
104};
105
106int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
107{
108 struct drm_encoder *encoder;
109 struct drm_bridge *bridge;
110 struct sun4i_lvds *lvds;
111 int ret;
112
113 lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
114 if (!lvds)
115 return -ENOMEM;
116 lvds->tcon = tcon;
117 encoder = &lvds->encoder;
118
119 ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
120 &tcon->panel, &bridge);
121 if (ret) {
122 dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n");
123 return 0;
124 }
125
126 drm_encoder_helper_add(&lvds->encoder,
127 &sun4i_lvds_enc_helper_funcs);
128 ret = drm_encoder_init(drm,
129 &lvds->encoder,
130 &sun4i_lvds_enc_funcs,
131 DRM_MODE_ENCODER_LVDS,
132 NULL);
133 if (ret) {
134 dev_err(drm->dev, "Couldn't initialise the lvds encoder\n");
135 goto err_out;
136 }
137
138 /* The LVDS encoder can only work with the TCON channel 0 */
139 lvds->encoder.possible_crtcs = BIT(drm_crtc_index(&tcon->crtc->crtc));
140
141 if (tcon->panel) {
142 drm_connector_helper_add(&lvds->connector,
143 &sun4i_lvds_con_helper_funcs);
144 ret = drm_connector_init(drm, &lvds->connector,
145 &sun4i_lvds_con_funcs,
146 DRM_MODE_CONNECTOR_LVDS);
147 if (ret) {
148 dev_err(drm->dev, "Couldn't initialise the lvds connector\n");
149 goto err_cleanup_connector;
150 }
151
152 drm_mode_connector_attach_encoder(&lvds->connector,
153 &lvds->encoder);
154
155 ret = drm_panel_attach(tcon->panel, &lvds->connector);
156 if (ret) {
157 dev_err(drm->dev, "Couldn't attach our panel\n");
158 goto err_cleanup_connector;
159 }
160 }
161
162 if (bridge) {
163 ret = drm_bridge_attach(encoder, bridge, NULL);
164 if (ret) {
165 dev_err(drm->dev, "Couldn't attach our bridge\n");
166 goto err_cleanup_connector;
167 }
168 }
169
170 return 0;
171
172err_cleanup_connector:
173 drm_encoder_cleanup(&lvds->encoder);
174err_out:
175 return ret;
176}
177EXPORT_SYMBOL(sun4i_lvds_init);
diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.h b/drivers/gpu/drm/sun4i/sun4i_lvds.h
new file mode 100644
index 000000000000..f3e90faa3082
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_lvds.h
@@ -0,0 +1,12 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2017 Free Electrons
4 * Maxime Ripard <maxime.ripard@free-electrons.com>
5 */
6
7#ifndef _SUN4I_LVDS_H_
8#define _SUN4I_LVDS_H_
9
10int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon);
11
12#endif /* _SUN4I_LVDS_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index a1ed462c2430..a897f82d9e66 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -31,10 +31,52 @@
31#include "sun4i_crtc.h" 31#include "sun4i_crtc.h"
32#include "sun4i_dotclock.h" 32#include "sun4i_dotclock.h"
33#include "sun4i_drv.h" 33#include "sun4i_drv.h"
34#include "sun4i_lvds.h"
34#include "sun4i_rgb.h" 35#include "sun4i_rgb.h"
35#include "sun4i_tcon.h" 36#include "sun4i_tcon.h"
36#include "sunxi_engine.h" 37#include "sunxi_engine.h"
37 38
39static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder)
40{
41 struct drm_connector *connector;
42 struct drm_connector_list_iter iter;
43
44 drm_connector_list_iter_begin(encoder->dev, &iter);
45 drm_for_each_connector_iter(connector, &iter)
46 if (connector->encoder == encoder) {
47 drm_connector_list_iter_end(&iter);
48 return connector;
49 }
50 drm_connector_list_iter_end(&iter);
51
52 return NULL;
53}
54
55static int sun4i_tcon_get_pixel_depth(const struct drm_encoder *encoder)
56{
57 struct drm_connector *connector;
58 struct drm_display_info *info;
59
60 connector = sun4i_tcon_get_connector(encoder);
61 if (!connector)
62 return -EINVAL;
63
64 info = &connector->display_info;
65 if (info->num_bus_formats != 1)
66 return -EINVAL;
67
68 switch (info->bus_formats[0]) {
69 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
70 return 18;
71
72 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
73 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
74 return 24;
75 }
76
77 return -EINVAL;
78}
79
38static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel, 80static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel,
39 bool enabled) 81 bool enabled)
40{ 82{
@@ -65,13 +107,63 @@ static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel,
65 clk_disable_unprepare(clk); 107 clk_disable_unprepare(clk);
66} 108}
67 109
110static void sun4i_tcon_lvds_set_status(struct sun4i_tcon *tcon,
111 const struct drm_encoder *encoder,
112 bool enabled)
113{
114 if (enabled) {
115 u8 val;
116
117 regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
118 SUN4I_TCON0_LVDS_IF_EN,
119 SUN4I_TCON0_LVDS_IF_EN);
120
121 /*
122 * As their name suggest, these values only apply to the A31
123 * and later SoCs. We'll have to rework this when merging
124 * support for the older SoCs.
125 */
126 regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
127 SUN6I_TCON0_LVDS_ANA0_C(2) |
128 SUN6I_TCON0_LVDS_ANA0_V(3) |
129 SUN6I_TCON0_LVDS_ANA0_PD(2) |
130 SUN6I_TCON0_LVDS_ANA0_EN_LDO);
131 udelay(2);
132
133 regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
134 SUN6I_TCON0_LVDS_ANA0_EN_MB,
135 SUN6I_TCON0_LVDS_ANA0_EN_MB);
136 udelay(2);
137
138 regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
139 SUN6I_TCON0_LVDS_ANA0_EN_DRVC,
140 SUN6I_TCON0_LVDS_ANA0_EN_DRVC);
141
142 if (sun4i_tcon_get_pixel_depth(encoder) == 18)
143 val = 7;
144 else
145 val = 0xf;
146
147 regmap_write_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
148 SUN6I_TCON0_LVDS_ANA0_EN_DRVD(0xf),
149 SUN6I_TCON0_LVDS_ANA0_EN_DRVD(val));
150 } else {
151 regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
152 SUN4I_TCON0_LVDS_IF_EN, 0);
153 }
154}
155
68void sun4i_tcon_set_status(struct sun4i_tcon *tcon, 156void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
69 const struct drm_encoder *encoder, 157 const struct drm_encoder *encoder,
70 bool enabled) 158 bool enabled)
71{ 159{
160 bool is_lvds = false;
72 int channel; 161 int channel;
73 162
74 switch (encoder->encoder_type) { 163 switch (encoder->encoder_type) {
164 case DRM_MODE_ENCODER_LVDS:
165 is_lvds = true;
166 /* Fallthrough */
75 case DRM_MODE_ENCODER_NONE: 167 case DRM_MODE_ENCODER_NONE:
76 channel = 0; 168 channel = 0;
77 break; 169 break;
@@ -84,10 +176,16 @@ void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
84 return; 176 return;
85 } 177 }
86 178
179 if (is_lvds && !enabled)
180 sun4i_tcon_lvds_set_status(tcon, encoder, false);
181
87 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 182 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
88 SUN4I_TCON_GCTL_TCON_ENABLE, 183 SUN4I_TCON_GCTL_TCON_ENABLE,
89 enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0); 184 enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);
90 185
186 if (is_lvds && enabled)
187 sun4i_tcon_lvds_set_status(tcon, encoder, true);
188
91 sun4i_tcon_channel_set_status(tcon, channel, enabled); 189 sun4i_tcon_channel_set_status(tcon, channel, enabled);
92} 190}
93 191
@@ -170,6 +268,75 @@ static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon,
170 SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); 268 SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
171} 269}
172 270
271static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
272 const struct drm_encoder *encoder,
273 const struct drm_display_mode *mode)
274{
275 unsigned int bp;
276 u8 clk_delay;
277 u32 reg, val = 0;
278
279 tcon->dclk_min_div = 7;
280 tcon->dclk_max_div = 7;
281 sun4i_tcon0_mode_set_common(tcon, mode);
282
283 /* Adjust clock delay */
284 clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
285 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
286 SUN4I_TCON0_CTL_CLK_DELAY_MASK,
287 SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
288
289 /*
290 * This is called a backporch in the register documentation,
291 * but it really is the back porch + hsync
292 */
293 bp = mode->crtc_htotal - mode->crtc_hsync_start;
294 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
295 mode->crtc_htotal, bp);
296
297 /* Set horizontal display timings */
298 regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,
299 SUN4I_TCON0_BASIC1_H_TOTAL(mode->htotal) |
300 SUN4I_TCON0_BASIC1_H_BACKPORCH(bp));
301
302 /*
303 * This is called a backporch in the register documentation,
304 * but it really is the back porch + hsync
305 */
306 bp = mode->crtc_vtotal - mode->crtc_vsync_start;
307 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
308 mode->crtc_vtotal, bp);
309
310 /* Set vertical display timings */
311 regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,
312 SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
313 SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
314
315 reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0 |
316 SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL |
317 SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL;
318 if (sun4i_tcon_get_pixel_depth(encoder) == 24)
319 reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS;
320 else
321 reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_18BITS;
322
323 regmap_write(tcon->regs, SUN4I_TCON0_LVDS_IF_REG, reg);
324
325 /* Setup the polarity of the various signals */
326 if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
327 val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;
328
329 if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
330 val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;
331
332 regmap_write(tcon->regs, SUN4I_TCON0_IO_POL_REG, val);
333
334 /* Map output pins to channel 0 */
335 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
336 SUN4I_TCON_GCTL_IOMAP_MASK,
337 SUN4I_TCON_GCTL_IOMAP_TCON0);
338}
339
173static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, 340static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,
174 const struct drm_display_mode *mode) 341 const struct drm_display_mode *mode)
175{ 342{
@@ -177,6 +344,8 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,
177 u8 clk_delay; 344 u8 clk_delay;
178 u32 val = 0; 345 u32 val = 0;
179 346
347 tcon->dclk_min_div = 6;
348 tcon->dclk_max_div = 127;
180 sun4i_tcon0_mode_set_common(tcon, mode); 349 sun4i_tcon0_mode_set_common(tcon, mode);
181 350
182 /* Adjust clock delay */ 351 /* Adjust clock delay */
@@ -334,6 +503,9 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
334 const struct drm_display_mode *mode) 503 const struct drm_display_mode *mode)
335{ 504{
336 switch (encoder->encoder_type) { 505 switch (encoder->encoder_type) {
506 case DRM_MODE_ENCODER_LVDS:
507 sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);
508 break;
337 case DRM_MODE_ENCODER_NONE: 509 case DRM_MODE_ENCODER_NONE:
338 sun4i_tcon0_mode_set_rgb(tcon, mode); 510 sun4i_tcon0_mode_set_rgb(tcon, mode);
339 sun4i_tcon_set_mux(tcon, 0, encoder); 511 sun4i_tcon_set_mux(tcon, 0, encoder);
@@ -665,7 +837,9 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
665 struct drm_device *drm = data; 837 struct drm_device *drm = data;
666 struct sun4i_drv *drv = drm->dev_private; 838 struct sun4i_drv *drv = drm->dev_private;
667 struct sunxi_engine *engine; 839 struct sunxi_engine *engine;
840 struct device_node *remote;
668 struct sun4i_tcon *tcon; 841 struct sun4i_tcon *tcon;
842 bool has_lvds_rst, has_lvds_alt, can_lvds;
669 int ret; 843 int ret;
670 844
671 engine = sun4i_tcon_find_engine(drv, dev->of_node); 845 engine = sun4i_tcon_find_engine(drv, dev->of_node);
@@ -696,6 +870,54 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
696 return ret; 870 return ret;
697 } 871 }
698 872
873 /*
874 * This can only be made optional since we've had DT nodes
875 * without the LVDS reset properties.
876 *
877 * If the property is missing, just disable LVDS, and print a
878 * warning.
879 */
880 tcon->lvds_rst = devm_reset_control_get_optional(dev, "lvds");
881 if (IS_ERR(tcon->lvds_rst)) {
882 dev_err(dev, "Couldn't get our reset line\n");
883 return PTR_ERR(tcon->lvds_rst);
884 } else if (tcon->lvds_rst) {
885 has_lvds_rst = true;
886 reset_control_reset(tcon->lvds_rst);
887 } else {
888 has_lvds_rst = false;
889 }
890
891 /*
892 * This can only be made optional since we've had DT nodes
893 * without the LVDS reset properties.
894 *
895 * If the property is missing, just disable LVDS, and print a
896 * warning.
897 */
898 if (tcon->quirks->has_lvds_alt) {
899 tcon->lvds_pll = devm_clk_get(dev, "lvds-alt");
900 if (IS_ERR(tcon->lvds_pll)) {
901 if (PTR_ERR(tcon->lvds_pll) == -ENOENT) {
902 has_lvds_alt = false;
903 } else {
904 dev_err(dev, "Couldn't get the LVDS PLL\n");
905 return PTR_ERR(tcon->lvds_rst);
906 }
907 } else {
908 has_lvds_alt = true;
909 }
910 }
911
912 if (!has_lvds_rst || (tcon->quirks->has_lvds_alt && !has_lvds_alt)) {
913 dev_warn(dev,
914 "Missing LVDS properties, Please upgrade your DT\n");
915 dev_warn(dev, "LVDS output disabled\n");
916 can_lvds = false;
917 } else {
918 can_lvds = true;
919 }
920
699 ret = sun4i_tcon_init_clocks(dev, tcon); 921 ret = sun4i_tcon_init_clocks(dev, tcon);
700 if (ret) { 922 if (ret) {
701 dev_err(dev, "Couldn't init our TCON clocks\n"); 923 dev_err(dev, "Couldn't init our TCON clocks\n");
@@ -727,7 +949,21 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
727 goto err_free_clocks; 949 goto err_free_clocks;
728 } 950 }
729 951
730 ret = sun4i_rgb_init(drm, tcon); 952 /*
953 * If we have an LVDS panel connected to the TCON, we should
954 * just probe the LVDS connector. Otherwise, just probe RGB as
955 * we used to.
956 */
957 remote = of_graph_get_remote_node(dev->of_node, 1, 0);
958 if (of_device_is_compatible(remote, "panel-lvds"))
959 if (can_lvds)
960 ret = sun4i_lvds_init(drm, tcon);
961 else
962 ret = -EINVAL;
963 else
964 ret = sun4i_rgb_init(drm, tcon);
965 of_node_put(remote);
966
731 if (ret < 0) 967 if (ret < 0)
732 goto err_free_clocks; 968 goto err_free_clocks;
733 969
@@ -877,6 +1113,7 @@ static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
877 1113
878static const struct sun4i_tcon_quirks sun6i_a31_quirks = { 1114static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
879 .has_channel_1 = true, 1115 .has_channel_1 = true,
1116 .has_lvds_alt = true,
880 .needs_de_be_mux = true, 1117 .needs_de_be_mux = true,
881 .set_mux = sun6i_tcon_set_mux, 1118 .set_mux = sun6i_tcon_set_mux,
882}; 1119};
@@ -893,6 +1130,10 @@ static const struct sun4i_tcon_quirks sun7i_a20_quirks = {
893}; 1130};
894 1131
895static const struct sun4i_tcon_quirks sun8i_a33_quirks = { 1132static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
1133 .has_lvds_alt = true,
1134};
1135
1136static const struct sun4i_tcon_quirks sun8i_a83t_lcd_quirks = {
896 /* nothing is supported */ 1137 /* nothing is supported */
897}; 1138};
898 1139
@@ -908,6 +1149,7 @@ const struct of_device_id sun4i_tcon_of_table[] = {
908 { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks }, 1149 { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks },
909 { .compatible = "allwinner,sun7i-a20-tcon", .data = &sun7i_a20_quirks }, 1150 { .compatible = "allwinner,sun7i-a20-tcon", .data = &sun7i_a20_quirks },
910 { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, 1151 { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },
1152 { .compatible = "allwinner,sun8i-a83t-tcon-lcd", .data = &sun8i_a83t_lcd_quirks },
911 { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, 1153 { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks },
912 { } 1154 { }
913}; 1155};
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
index 839266a38505..b761c7b823c5 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -70,7 +70,21 @@
70#define SUN4I_TCON0_TTL2_REG 0x78 70#define SUN4I_TCON0_TTL2_REG 0x78
71#define SUN4I_TCON0_TTL3_REG 0x7c 71#define SUN4I_TCON0_TTL3_REG 0x7c
72#define SUN4I_TCON0_TTL4_REG 0x80 72#define SUN4I_TCON0_TTL4_REG 0x80
73
73#define SUN4I_TCON0_LVDS_IF_REG 0x84 74#define SUN4I_TCON0_LVDS_IF_REG 0x84
75#define SUN4I_TCON0_LVDS_IF_EN BIT(31)
76#define SUN4I_TCON0_LVDS_IF_BITWIDTH_MASK BIT(26)
77#define SUN4I_TCON0_LVDS_IF_BITWIDTH_18BITS (1 << 26)
78#define SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS (0 << 26)
79#define SUN4I_TCON0_LVDS_IF_CLK_SEL_MASK BIT(20)
80#define SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0 (1 << 20)
81#define SUN4I_TCON0_LVDS_IF_CLK_POL_MASK BIT(4)
82#define SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL (1 << 4)
83#define SUN4I_TCON0_LVDS_IF_CLK_POL_INV (0 << 4)
84#define SUN4I_TCON0_LVDS_IF_DATA_POL_MASK GENMASK(3, 0)
85#define SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL (0xf)
86#define SUN4I_TCON0_LVDS_IF_DATA_POL_INV (0)
87
74#define SUN4I_TCON0_IO_POL_REG 0x88 88#define SUN4I_TCON0_IO_POL_REG 0x88
75#define SUN4I_TCON0_IO_POL_DCLK_PHASE(phase) ((phase & 3) << 28) 89#define SUN4I_TCON0_IO_POL_DCLK_PHASE(phase) ((phase & 3) << 28)
76#define SUN4I_TCON0_IO_POL_HSYNC_POSITIVE BIT(25) 90#define SUN4I_TCON0_IO_POL_HSYNC_POSITIVE BIT(25)
@@ -131,6 +145,16 @@
131#define SUN4I_TCON_CEU_RANGE_G_REG 0x144 145#define SUN4I_TCON_CEU_RANGE_G_REG 0x144
132#define SUN4I_TCON_CEU_RANGE_B_REG 0x148 146#define SUN4I_TCON_CEU_RANGE_B_REG 0x148
133#define SUN4I_TCON_MUX_CTRL_REG 0x200 147#define SUN4I_TCON_MUX_CTRL_REG 0x200
148
149#define SUN4I_TCON0_LVDS_ANA0_REG 0x220
150#define SUN6I_TCON0_LVDS_ANA0_EN_MB BIT(31)
151#define SUN6I_TCON0_LVDS_ANA0_EN_LDO BIT(30)
152#define SUN6I_TCON0_LVDS_ANA0_EN_DRVC BIT(24)
153#define SUN6I_TCON0_LVDS_ANA0_EN_DRVD(x) (((x) & 0xf) << 20)
154#define SUN6I_TCON0_LVDS_ANA0_C(x) (((x) & 3) << 17)
155#define SUN6I_TCON0_LVDS_ANA0_V(x) (((x) & 3) << 8)
156#define SUN6I_TCON0_LVDS_ANA0_PD(x) (((x) & 3) << 4)
157
134#define SUN4I_TCON1_FILL_CTL_REG 0x300 158#define SUN4I_TCON1_FILL_CTL_REG 0x300
135#define SUN4I_TCON1_FILL_BEG0_REG 0x304 159#define SUN4I_TCON1_FILL_BEG0_REG 0x304
136#define SUN4I_TCON1_FILL_END0_REG 0x308 160#define SUN4I_TCON1_FILL_END0_REG 0x308
@@ -149,6 +173,7 @@ struct sun4i_tcon;
149 173
150struct sun4i_tcon_quirks { 174struct sun4i_tcon_quirks {
151 bool has_channel_1; /* a33 does not have channel 1 */ 175 bool has_channel_1; /* a33 does not have channel 1 */
176 bool has_lvds_alt; /* Does the LVDS clock have a parent other than the TCON clock? */
152 bool needs_de_be_mux; /* sun6i needs mux to select backend */ 177 bool needs_de_be_mux; /* sun6i needs mux to select backend */
153 178
154 /* callback to handle tcon muxing options */ 179 /* callback to handle tcon muxing options */
@@ -167,11 +192,17 @@ struct sun4i_tcon {
167 struct clk *sclk0; 192 struct clk *sclk0;
168 struct clk *sclk1; 193 struct clk *sclk1;
169 194
195 /* Possible mux for the LVDS clock */
196 struct clk *lvds_pll;
197
170 /* Pixel clock */ 198 /* Pixel clock */
171 struct clk *dclk; 199 struct clk *dclk;
200 u8 dclk_max_div;
201 u8 dclk_min_div;
172 202
173 /* Reset control */ 203 /* Reset control */
174 struct reset_control *lcd_rst; 204 struct reset_control *lcd_rst;
205 struct reset_control *lvds_rst;
175 206
176 struct drm_panel *panel; 207 struct drm_panel *panel;
177 208
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 29ceeb016d72..2cbb2de6d39c 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -398,6 +398,15 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
398 ret = PTR_ERR(mixer->mod_clk); 398 ret = PTR_ERR(mixer->mod_clk);
399 goto err_disable_bus_clk; 399 goto err_disable_bus_clk;
400 } 400 }
401
402 /*
403 * It seems that we need to enforce that rate for whatever
404 * reason for the mixer to be functional. Make sure it's the
405 * case.
406 */
407 if (mixer->cfg->mod_rate)
408 clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate);
409
401 clk_prepare_enable(mixer->mod_clk); 410 clk_prepare_enable(mixer->mod_clk);
402 411
403 list_add_tail(&mixer->engine.list, &drv->engine_list); 412 list_add_tail(&mixer->engine.list, &drv->engine_list);
@@ -469,15 +478,27 @@ static int sun8i_mixer_remove(struct platform_device *pdev)
469 return 0; 478 return 0;
470} 479}
471 480
481static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
482 .ccsc = 0,
483 .scaler_mask = 0xf,
484 .ui_num = 3,
485 .vi_num = 1,
486};
487
472static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { 488static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
473 .vi_num = 2, 489 .vi_num = 2,
474 .ui_num = 1, 490 .ui_num = 1,
475 .scaler_mask = 0x3, 491 .scaler_mask = 0x3,
476 .ccsc = 0, 492 .ccsc = 0,
493 .mod_rate = 150000000,
477}; 494};
478 495
479static const struct of_device_id sun8i_mixer_of_table[] = { 496static const struct of_device_id sun8i_mixer_of_table[] = {
480 { 497 {
498 .compatible = "allwinner,sun8i-a83t-de2-mixer-0",
499 .data = &sun8i_a83t_mixer0_cfg,
500 },
501 {
481 .compatible = "allwinner,sun8i-v3s-de2-mixer", 502 .compatible = "allwinner,sun8i-v3s-de2-mixer",
482 .data = &sun8i_v3s_mixer_cfg, 503 .data = &sun8i_v3s_mixer_cfg,
483 }, 504 },
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index bc58040a88f9..f34e70c42adf 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -121,12 +121,15 @@ struct de2_fmt_info {
121 * Set value to 0 if this is first mixer or second mixer with VEP support. 121 * Set value to 0 if this is first mixer or second mixer with VEP support.
122 * Set value to 1 if this is second mixer without VEP support. Other values 122 * Set value to 1 if this is second mixer without VEP support. Other values
123 * are invalid. 123 * are invalid.
124 * @mod_rate: module clock rate that needs to be set in order to have
125 * a functional block.
124 */ 126 */
125struct sun8i_mixer_cfg { 127struct sun8i_mixer_cfg {
126 int vi_num; 128 int vi_num;
127 int ui_num; 129 int ui_num;
128 int scaler_mask; 130 int scaler_mask;
129 int ccsc; 131 int ccsc;
132 unsigned long mod_rate;
130}; 133};
131 134
132struct sun8i_mixer { 135struct sun8i_mixer {
diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig
index 90c5bd5ef81b..b0e567d416b3 100644
--- a/drivers/gpu/drm/tinydrm/Kconfig
+++ b/drivers/gpu/drm/tinydrm/Kconfig
@@ -52,3 +52,13 @@ config TINYDRM_ST7586
52 * LEGO MINDSTORMS EV3 52 * LEGO MINDSTORMS EV3
53 53
54 If M is selected the module will be called st7586. 54 If M is selected the module will be called st7586.
55
56config TINYDRM_ST7735R
57 tristate "DRM support for Sitronix ST7735R display panels"
58 depends on DRM_TINYDRM && SPI
59 select TINYDRM_MIPI_DBI
60 help
61 DRM driver Sitronix ST7735R with one of the following LCDs:
62 * JD-T18003-T01 1.8" 128x160 TFT
63
64 If M is selected the module will be called st7735r.
diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile
index 8aeee532474f..49a111929724 100644
--- a/drivers/gpu/drm/tinydrm/Makefile
+++ b/drivers/gpu/drm/tinydrm/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o
8obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o 8obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o
9obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o 9obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o
10obj-$(CONFIG_TINYDRM_ST7586) += st7586.o 10obj-$(CONFIG_TINYDRM_ST7586) += st7586.o
11obj-$(CONFIG_TINYDRM_ST7735R) += st7735r.o
diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c
index e8f1b3af3852..c0cf49849302 100644
--- a/drivers/gpu/drm/tinydrm/ili9225.c
+++ b/drivers/gpu/drm/tinydrm/ili9225.c
@@ -391,13 +391,13 @@ static struct drm_driver ili9225_driver = {
391}; 391};
392 392
393static const struct of_device_id ili9225_of_match[] = { 393static const struct of_device_id ili9225_of_match[] = {
394 { .compatible = "ilitek,ili9225-2.2in-176x220" }, 394 { .compatible = "vot,v220hf01a-t" },
395 {}, 395 {},
396}; 396};
397MODULE_DEVICE_TABLE(of, ili9225_of_match); 397MODULE_DEVICE_TABLE(of, ili9225_of_match);
398 398
399static const struct spi_device_id ili9225_id[] = { 399static const struct spi_device_id ili9225_id[] = {
400 { "ili9225-2.2in-176x220", 0 }, 400 { "v220hf01a-t", 0 },
401 { }, 401 { },
402}; 402};
403MODULE_DEVICE_TABLE(spi, ili9225_id); 403MODULE_DEVICE_TABLE(spi, ili9225_id);
diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c
new file mode 100644
index 000000000000..98ff447f40b4
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/st7735r.c
@@ -0,0 +1,215 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * DRM driver for Sitronix ST7735R panels
4 *
5 * Copyright 2017 David Lechner <david@lechnology.com>
6 */
7
8#include <linux/delay.h>
9#include <linux/dma-buf.h>
10#include <linux/gpio/consumer.h>
11#include <linux/module.h>
12#include <linux/property.h>
13#include <linux/spi/spi.h>
14#include <video/mipi_display.h>
15
16#include <drm/drm_fb_helper.h>
17#include <drm/drm_gem_framebuffer_helper.h>
18#include <drm/tinydrm/mipi-dbi.h>
19#include <drm/tinydrm/tinydrm-helpers.h>
20
21#define ST7735R_FRMCTR1 0xb1
22#define ST7735R_FRMCTR2 0xb2
23#define ST7735R_FRMCTR3 0xb3
24#define ST7735R_INVCTR 0xb4
25#define ST7735R_PWCTR1 0xc0
26#define ST7735R_PWCTR2 0xc1
27#define ST7735R_PWCTR3 0xc2
28#define ST7735R_PWCTR4 0xc3
29#define ST7735R_PWCTR5 0xc4
30#define ST7735R_VMCTR1 0xc5
31#define ST7735R_GAMCTRP1 0xe0
32#define ST7735R_GAMCTRN1 0xe1
33
34#define ST7735R_MY BIT(7)
35#define ST7735R_MX BIT(6)
36#define ST7735R_MV BIT(5)
37
38static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe,
39 struct drm_crtc_state *crtc_state)
40{
41 struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
42 struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
43 struct device *dev = tdev->drm->dev;
44 int ret;
45 u8 addr_mode;
46
47 DRM_DEBUG_KMS("\n");
48
49 mipi_dbi_hw_reset(mipi);
50
51 ret = mipi_dbi_command(mipi, MIPI_DCS_SOFT_RESET);
52 if (ret) {
53 DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
54 return;
55 }
56
57 msleep(150);
58
59 mipi_dbi_command(mipi, MIPI_DCS_EXIT_SLEEP_MODE);
60 msleep(500);
61
62 mipi_dbi_command(mipi, ST7735R_FRMCTR1, 0x01, 0x2c, 0x2d);
63 mipi_dbi_command(mipi, ST7735R_FRMCTR2, 0x01, 0x2c, 0x2d);
64 mipi_dbi_command(mipi, ST7735R_FRMCTR3, 0x01, 0x2c, 0x2d, 0x01, 0x2c,
65 0x2d);
66 mipi_dbi_command(mipi, ST7735R_INVCTR, 0x07);
67 mipi_dbi_command(mipi, ST7735R_PWCTR1, 0xa2, 0x02, 0x84);
68 mipi_dbi_command(mipi, ST7735R_PWCTR2, 0xc5);
69 mipi_dbi_command(mipi, ST7735R_PWCTR3, 0x0a, 0x00);
70 mipi_dbi_command(mipi, ST7735R_PWCTR4, 0x8a, 0x2a);
71 mipi_dbi_command(mipi, ST7735R_PWCTR5, 0x8a, 0xee);
72 mipi_dbi_command(mipi, ST7735R_VMCTR1, 0x0e);
73 mipi_dbi_command(mipi, MIPI_DCS_EXIT_INVERT_MODE);
74 switch (mipi->rotation) {
75 default:
76 addr_mode = ST7735R_MX | ST7735R_MY;
77 break;
78 case 90:
79 addr_mode = ST7735R_MX | ST7735R_MV;
80 break;
81 case 180:
82 addr_mode = 0;
83 break;
84 case 270:
85 addr_mode = ST7735R_MY | ST7735R_MV;
86 break;
87 }
88 mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
89 mipi_dbi_command(mipi, MIPI_DCS_SET_PIXEL_FORMAT,
90 MIPI_DCS_PIXEL_FMT_16BIT);
91 mipi_dbi_command(mipi, ST7735R_GAMCTRP1, 0x02, 0x1c, 0x07, 0x12, 0x37,
92 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2b, 0x39, 0x00, 0x01,
93 0x03, 0x10);
94 mipi_dbi_command(mipi, ST7735R_GAMCTRN1, 0x03, 0x1d, 0x07, 0x06, 0x2e,
95 0x2c, 0x29, 0x2d, 0x2e, 0x2e, 0x37, 0x3f, 0x00, 0x00,
96 0x02, 0x10);
97 mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
98
99 msleep(100);
100
101 mipi_dbi_command(mipi, MIPI_DCS_ENTER_NORMAL_MODE);
102
103 msleep(20);
104
105 mipi_dbi_pipe_enable(pipe, crtc_state);
106}
107
108static const struct drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = {
109 .enable = jd_t18003_t01_pipe_enable,
110 .disable = mipi_dbi_pipe_disable,
111 .update = tinydrm_display_pipe_update,
112 .prepare_fb = tinydrm_display_pipe_prepare_fb,
113};
114
115static const struct drm_display_mode jd_t18003_t01_mode = {
116 TINYDRM_MODE(128, 160, 28, 35),
117};
118
119DEFINE_DRM_GEM_CMA_FOPS(st7735r_fops);
120
121static struct drm_driver st7735r_driver = {
122 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
123 DRIVER_ATOMIC,
124 .fops = &st7735r_fops,
125 TINYDRM_GEM_DRIVER_OPS,
126 .lastclose = drm_fb_helper_lastclose,
127 .debugfs_init = mipi_dbi_debugfs_init,
128 .name = "st7735r",
129 .desc = "Sitronix ST7735R",
130 .date = "20171128",
131 .major = 1,
132 .minor = 0,
133};
134
135static const struct of_device_id st7735r_of_match[] = {
136 { .compatible = "jianda,jd-t18003-t01" },
137 { },
138};
139MODULE_DEVICE_TABLE(of, st7735r_of_match);
140
141static const struct spi_device_id st7735r_id[] = {
142 { "jd-t18003-t01", 0 },
143 { },
144};
145MODULE_DEVICE_TABLE(spi, st7735r_id);
146
147static int st7735r_probe(struct spi_device *spi)
148{
149 struct device *dev = &spi->dev;
150 struct mipi_dbi *mipi;
151 struct gpio_desc *dc;
152 u32 rotation = 0;
153 int ret;
154
155 mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
156 if (!mipi)
157 return -ENOMEM;
158
159 mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
160 if (IS_ERR(mipi->reset)) {
161 DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
162 return PTR_ERR(mipi->reset);
163 }
164
165 dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
166 if (IS_ERR(dc)) {
167 DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n");
168 return PTR_ERR(dc);
169 }
170
171 mipi->backlight = tinydrm_of_find_backlight(dev);
172 if (IS_ERR(mipi->backlight))
173 return PTR_ERR(mipi->backlight);
174
175 device_property_read_u32(dev, "rotation", &rotation);
176
177 ret = mipi_dbi_spi_init(spi, mipi, dc);
178 if (ret)
179 return ret;
180
181 /* Cannot read from Adafruit 1.8" display via SPI */
182 mipi->read_commands = NULL;
183
184 ret = mipi_dbi_init(&spi->dev, mipi, &jd_t18003_t01_pipe_funcs,
185 &st7735r_driver, &jd_t18003_t01_mode, rotation);
186 if (ret)
187 return ret;
188
189 spi_set_drvdata(spi, mipi);
190
191 return devm_tinydrm_register(&mipi->tinydrm);
192}
193
194static void st7735r_shutdown(struct spi_device *spi)
195{
196 struct mipi_dbi *mipi = spi_get_drvdata(spi);
197
198 tinydrm_shutdown(&mipi->tinydrm);
199}
200
201static struct spi_driver st7735r_spi_driver = {
202 .driver = {
203 .name = "st7735r",
204 .owner = THIS_MODULE,
205 .of_match_table = st7735r_of_match,
206 },
207 .id_table = st7735r_id,
208 .probe = st7735r_probe,
209 .shutdown = st7735r_shutdown,
210};
211module_spi_driver(st7735r_spi_driver);
212
213MODULE_DESCRIPTION("Sitronix ST7735R DRM driver");
214MODULE_AUTHOR("David Lechner <david@lechnology.com>");
215MODULE_LICENSE("GPL");