aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2016-06-16 05:36:17 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2016-08-11 10:54:54 -0400
commit1d3f0cbe0d3a537cbc8fb1a91f6d14407acece62 (patch)
tree5967571cd10063d3c246683ada23a1e261a311e1 /drivers/video
parent046ad6cdeb3f83abcbfa2af88ce471afb2e7fc30 (diff)
video: ARM CLCD: add special board and panel hooks for Nomadik
In the .board_init() callback will set up a mux register in the Nomadik system controller. It so happens that the platform has two display output engines, and we have to poke a bit in a special register to make sure the right engine is muxed in as they are mutually exclusive. The Nomadik CLCD variant is instantiated on a platform where it is combined with a 800x480 TPO WVGA display. In the .panel_init() hook we will detect this display from the compatible string and set it up. We also add .enable() and .disable() callbacks for it as the sleep state is software controlled. The display is connected with a special 3-wire serial bus (this is sadly neither I2C or SPI) using three GPIO lines that we bitbang to detect the display and enable/disable sleep state. Cc: Pawel Moll <pawel.moll@arm.com> Cc: Rob Herring <robh@kernel.org> Cc: Russell King <linux@arm.linux.org.uk> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/fbdev/Makefile1
-rw-r--r--drivers/video/fbdev/amba-clcd-nomadik.c257
-rw-r--r--drivers/video/fbdev/amba-clcd-nomadik.h24
-rw-r--r--drivers/video/fbdev/amba-clcd.c6
4 files changed, 287 insertions, 1 deletions
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index f6731867dd26..5550944f0ad7 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o
79obj-$(CONFIG_FB_PVR2) += pvr2fb.o 79obj-$(CONFIG_FB_PVR2) += pvr2fb.o
80obj-$(CONFIG_FB_VOODOO1) += sstfb.o 80obj-$(CONFIG_FB_VOODOO1) += sstfb.o
81obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o 81obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o
82obj-$(CONFIG_ARCH_NOMADIK) += amba-clcd-nomadik.o
82obj-$(CONFIG_PLAT_VERSATILE_CLCD) += amba-clcd-versatile.o 83obj-$(CONFIG_PLAT_VERSATILE_CLCD) += amba-clcd-versatile.o
83obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o 84obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o
84obj-$(CONFIG_FB_68328) += 68328fb.o 85obj-$(CONFIG_FB_68328) += 68328fb.o
diff --git a/drivers/video/fbdev/amba-clcd-nomadik.c b/drivers/video/fbdev/amba-clcd-nomadik.c
new file mode 100644
index 000000000000..243167c8ee15
--- /dev/null
+++ b/drivers/video/fbdev/amba-clcd-nomadik.c
@@ -0,0 +1,257 @@
1#include <linux/amba/bus.h>
2#include <linux/amba/clcd.h>
3#include <linux/gpio/consumer.h>
4#include <linux/of.h>
5#include <linux/of_graph.h>
6#include <linux/delay.h>
7#include <linux/bitops.h>
8#include <linux/mfd/syscon.h>
9#include <linux/regmap.h>
10
11#include "amba-clcd-nomadik.h"
12
13static struct gpio_desc *grestb;
14static struct gpio_desc *scen;
15static struct gpio_desc *scl;
16static struct gpio_desc *sda;
17
18static u8 tpg110_readwrite_reg(bool write, u8 address, u8 outval)
19{
20 int i;
21 u8 inval = 0;
22
23 /* Assert SCEN */
24 gpiod_set_value_cansleep(scen, 1);
25 ndelay(150);
26 /* Hammer out the address */
27 for (i = 5; i >= 0; i--) {
28 if (address & BIT(i))
29 gpiod_set_value_cansleep(sda, 1);
30 else
31 gpiod_set_value_cansleep(sda, 0);
32 ndelay(150);
33 /* Send an SCL pulse */
34 gpiod_set_value_cansleep(scl, 1);
35 ndelay(160);
36 gpiod_set_value_cansleep(scl, 0);
37 ndelay(160);
38 }
39
40 if (write) {
41 /* WRITE */
42 gpiod_set_value_cansleep(sda, 0);
43 } else {
44 /* READ */
45 gpiod_set_value_cansleep(sda, 1);
46 }
47 ndelay(150);
48 /* Send an SCL pulse */
49 gpiod_set_value_cansleep(scl, 1);
50 ndelay(160);
51 gpiod_set_value_cansleep(scl, 0);
52 ndelay(160);
53
54 if (!write)
55 /* HiZ turn-around cycle */
56 gpiod_direction_input(sda);
57 ndelay(150);
58 /* Send an SCL pulse */
59 gpiod_set_value_cansleep(scl, 1);
60 ndelay(160);
61 gpiod_set_value_cansleep(scl, 0);
62 ndelay(160);
63
64 /* Hammer in/out the data */
65 for (i = 7; i >= 0; i--) {
66 int value;
67
68 if (write) {
69 value = !!(outval & BIT(i));
70 gpiod_set_value_cansleep(sda, value);
71 } else {
72 value = gpiod_get_value(sda);
73 if (value)
74 inval |= BIT(i);
75 }
76 ndelay(150);
77 /* Send an SCL pulse */
78 gpiod_set_value_cansleep(scl, 1);
79 ndelay(160);
80 gpiod_set_value_cansleep(scl, 0);
81 ndelay(160);
82 }
83
84 gpiod_direction_output(sda, 0);
85 /* Deassert SCEN */
86 gpiod_set_value_cansleep(scen, 0);
87 /* Satisfies SCEN pulse width */
88 udelay(1);
89
90 return inval;
91}
92
93static u8 tpg110_read_reg(u8 address)
94{
95 return tpg110_readwrite_reg(false, address, 0);
96}
97
98static void tpg110_write_reg(u8 address, u8 outval)
99{
100 tpg110_readwrite_reg(true, address, outval);
101}
102
103static void tpg110_startup(struct device *dev)
104{
105 u8 val;
106
107 dev_info(dev, "TPG110 display enable\n");
108 /* De-assert the reset signal */
109 gpiod_set_value_cansleep(grestb, 0);
110 mdelay(1);
111 dev_info(dev, "de-asserted GRESTB\n");
112
113 /* Test display communication */
114 tpg110_write_reg(0x00, 0x55);
115 val = tpg110_read_reg(0x00);
116 if (val == 0x55)
117 dev_info(dev, "passed communication test\n");
118 val = tpg110_read_reg(0x01);
119 dev_info(dev, "TPG110 chip ID: %d version: %d\n",
120 val>>4, val&0x0f);
121
122 /* Show display resolution */
123 val = tpg110_read_reg(0x02);
124 val &= 7;
125 switch (val) {
126 case 0x0:
127 dev_info(dev, "IN 400x240 RGB -> OUT 800x480 RGB (dual scan)");
128 break;
129 case 0x1:
130 dev_info(dev, "IN 480x272 RGB -> OUT 800x480 RGB (dual scan)");
131 break;
132 case 0x4:
133 dev_info(dev, "480x640 RGB");
134 break;
135 case 0x5:
136 dev_info(dev, "480x272 RGB");
137 break;
138 case 0x6:
139 dev_info(dev, "640x480 RGB");
140 break;
141 case 0x7:
142 dev_info(dev, "800x480 RGB");
143 break;
144 default:
145 dev_info(dev, "ILLEGAL RESOLUTION");
146 break;
147 }
148
149 val = tpg110_read_reg(0x03);
150 dev_info(dev, "resolution is controlled by %s\n",
151 (val & BIT(7)) ? "software" : "hardware");
152}
153
154static void tpg110_enable(struct clcd_fb *fb)
155{
156 struct device *dev = &fb->dev->dev;
157 static bool startup;
158 u8 val;
159
160 if (!startup) {
161 tpg110_startup(dev);
162 startup = true;
163 }
164
165 /* Take chip out of standby */
166 val = tpg110_read_reg(0x03);
167 val |= BIT(0);
168 tpg110_write_reg(0x03, val);
169}
170
171static void tpg110_disable(struct clcd_fb *fb)
172{
173 u8 val;
174
175 dev_info(&fb->dev->dev, "TPG110 display disable\n");
176 val = tpg110_read_reg(0x03);
177 /* Put into standby */
178 val &= ~BIT(0);
179 tpg110_write_reg(0x03, val);
180}
181
182static void tpg110_init(struct device *dev, struct device_node *np,
183 struct clcd_board *board)
184{
185 dev_info(dev, "TPG110 display init\n");
186
187 grestb = devm_get_gpiod_from_child(dev, "grestb", &np->fwnode);
188 if (IS_ERR(grestb)) {
189 dev_err(dev, "no GRESTB GPIO\n");
190 return;
191 }
192 /* This asserts the GRESTB signal, putting the display into reset */
193 gpiod_direction_output(grestb, 1);
194
195 scen = devm_get_gpiod_from_child(dev, "scen", &np->fwnode);
196 if (IS_ERR(scen)) {
197 dev_err(dev, "no SCEN GPIO\n");
198 return;
199 }
200 gpiod_direction_output(scen, 0);
201 scl = devm_get_gpiod_from_child(dev, "scl", &np->fwnode);
202 if (IS_ERR(scl)) {
203 dev_err(dev, "no SCL GPIO\n");
204 return;
205 }
206 gpiod_direction_output(scl, 0);
207 sda = devm_get_gpiod_from_child(dev, "sda", &np->fwnode);
208 if (IS_ERR(sda)) {
209 dev_err(dev, "no SDA GPIO\n");
210 return;
211 }
212 gpiod_direction_output(sda, 0);
213 board->enable = tpg110_enable;
214 board->disable = tpg110_disable;
215}
216
217int nomadik_clcd_init_panel(struct clcd_fb *fb,
218 struct device_node *endpoint)
219{
220 struct device_node *panel;
221
222 panel = of_graph_get_remote_port_parent(endpoint);
223 if (!panel)
224 return -ENODEV;
225
226 if (of_device_is_compatible(panel, "tpo,tpg110"))
227 tpg110_init(&fb->dev->dev, panel, fb->board);
228 else
229 dev_info(&fb->dev->dev, "unknown panel\n");
230
231 /* Unknown panel, fall through */
232 return 0;
233}
234
235#define PMU_CTRL_OFFSET 0x0000
236#define PMU_CTRL_LCDNDIF BIT(26)
237
238int nomadik_clcd_init_board(struct amba_device *adev,
239 struct clcd_board *board)
240{
241 struct regmap *pmu_regmap;
242
243 dev_info(&adev->dev, "Nomadik CLCD board init\n");
244 pmu_regmap =
245 syscon_regmap_lookup_by_compatible("stericsson,nomadik-pmu");
246 if (IS_ERR(pmu_regmap)) {
247 dev_err(&adev->dev, "could not find PMU syscon regmap\n");
248 return PTR_ERR(pmu_regmap);
249 }
250 regmap_update_bits(pmu_regmap,
251 PMU_CTRL_OFFSET,
252 PMU_CTRL_LCDNDIF,
253 0);
254 dev_info(&adev->dev, "set PMU mux to CLCD mode\n");
255
256 return 0;
257}
diff --git a/drivers/video/fbdev/amba-clcd-nomadik.h b/drivers/video/fbdev/amba-clcd-nomadik.h
new file mode 100644
index 000000000000..50aa9bda69fd
--- /dev/null
+++ b/drivers/video/fbdev/amba-clcd-nomadik.h
@@ -0,0 +1,24 @@
1#ifndef _AMBA_CLCD_NOMADIK_H
2#define _AMBA_CLCD_NOMADIK_H
3
4#include <linux/amba/bus.h>
5
6#ifdef CONFIG_ARCH_NOMADIK
7int nomadik_clcd_init_board(struct amba_device *adev,
8 struct clcd_board *board);
9int nomadik_clcd_init_panel(struct clcd_fb *fb,
10 struct device_node *endpoint);
11#else
12static inline int nomadik_clcd_init_board(struct amba_device *adev,
13 struct clcd_board *board)
14{
15 return 0;
16}
17static inline int nomadik_clcd_init_panel(struct clcd_fb *fb,
18 struct device_node *endpoint)
19{
20 return 0;
21}
22#endif
23
24#endif /* inclusion guard */
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c
index 371f4e2aea6c..2b45c7be4815 100644
--- a/drivers/video/fbdev/amba-clcd.c
+++ b/drivers/video/fbdev/amba-clcd.c
@@ -35,6 +35,8 @@
35#include <video/of_display_timing.h> 35#include <video/of_display_timing.h>
36#include <video/videomode.h> 36#include <video/videomode.h>
37 37
38#include "amba-clcd-nomadik.h"
39
38#define to_clcd(info) container_of(info, struct clcd_fb, fb) 40#define to_clcd(info) container_of(info, struct clcd_fb, fb)
39 41
40/* This is limited to 16 characters when displayed by X startup */ 42/* This is limited to 16 characters when displayed by X startup */
@@ -997,7 +999,7 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
997 if (ret) 999 if (ret)
998 goto free_fb; 1000 goto free_fb;
999 1001
1000 ret = clcdfb_register(fb); 1002 ret = clcdfb_register(fb);
1001 if (ret == 0) { 1003 if (ret == 0) {
1002 amba_set_drvdata(dev, fb); 1004 amba_set_drvdata(dev, fb);
1003 goto out; 1005 goto out;
@@ -1041,6 +1043,8 @@ static struct clcd_vendor_data vendor_nomadik = {
1041 .clock_timregs = true, 1043 .clock_timregs = true,
1042 .packed_24_bit_pixels = true, 1044 .packed_24_bit_pixels = true,
1043 .st_bitmux_control = true, 1045 .st_bitmux_control = true,
1046 .init_board = nomadik_clcd_init_board,
1047 .init_panel = nomadik_clcd_init_panel,
1044}; 1048};
1045 1049
1046static struct amba_id clcdfb_id_table[] = { 1050static struct amba_id clcdfb_id_table[] = {