Linux-libre 4.14.14-gnu
[librecmc/linux-libre.git] / drivers / video / fbdev / amba-clcd-nomadik.c
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
13 static struct gpio_desc *grestb;
14 static struct gpio_desc *scen;
15 static struct gpio_desc *scl;
16 static struct gpio_desc *sda;
17
18 static 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
93 static u8 tpg110_read_reg(u8 address)
94 {
95         return tpg110_readwrite_reg(false, address, 0);
96 }
97
98 static void tpg110_write_reg(u8 address, u8 outval)
99 {
100         tpg110_readwrite_reg(true, address, outval);
101 }
102
103 static 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
154 static 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
171 static 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
182 static 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         /* This asserts the GRESTB signal, putting the display into reset */
188         grestb = devm_fwnode_get_gpiod_from_child(dev, "grestb", &np->fwnode,
189                                                   GPIOD_OUT_HIGH, "grestb");
190         if (IS_ERR(grestb)) {
191                 dev_err(dev, "no GRESTB GPIO\n");
192                 return;
193         }
194         scen = devm_fwnode_get_gpiod_from_child(dev, "scen", &np->fwnode,
195                                                 GPIOD_OUT_LOW, "scen");
196         if (IS_ERR(scen)) {
197                 dev_err(dev, "no SCEN GPIO\n");
198                 return;
199         }
200         scl = devm_fwnode_get_gpiod_from_child(dev, "scl", &np->fwnode,
201                                                GPIOD_OUT_LOW, "scl");
202         if (IS_ERR(scl)) {
203                 dev_err(dev, "no SCL GPIO\n");
204                 return;
205         }
206         sda = devm_fwnode_get_gpiod_from_child(dev, "sda", &np->fwnode,
207                                                GPIOD_OUT_LOW, "sda");
208         if (IS_ERR(sda)) {
209                 dev_err(dev, "no SDA GPIO\n");
210                 return;
211         }
212         board->enable = tpg110_enable;
213         board->disable = tpg110_disable;
214 }
215
216 int nomadik_clcd_init_panel(struct clcd_fb *fb, struct device_node *panel)
217 {
218         if (of_device_is_compatible(panel, "tpo,tpg110"))
219                 tpg110_init(&fb->dev->dev, panel, fb->board);
220         else
221                 dev_info(&fb->dev->dev, "unknown panel\n");
222
223         /* Unknown panel, fall through */
224         return 0;
225 }
226 EXPORT_SYMBOL_GPL(nomadik_clcd_init_panel);
227
228 #define PMU_CTRL_OFFSET 0x0000
229 #define PMU_CTRL_LCDNDIF BIT(26)
230
231 int nomadik_clcd_init_board(struct amba_device *adev,
232                             struct clcd_board *board)
233 {
234         struct regmap *pmu_regmap;
235
236         dev_info(&adev->dev, "Nomadik CLCD board init\n");
237         pmu_regmap =
238                 syscon_regmap_lookup_by_compatible("stericsson,nomadik-pmu");
239         if (IS_ERR(pmu_regmap)) {
240                 dev_err(&adev->dev, "could not find PMU syscon regmap\n");
241                 return PTR_ERR(pmu_regmap);
242         }
243         regmap_update_bits(pmu_regmap,
244                            PMU_CTRL_OFFSET,
245                            PMU_CTRL_LCDNDIF,
246                            0);
247         dev_info(&adev->dev, "set PMU mux to CLCD mode\n");
248
249         return 0;
250 }
251 EXPORT_SYMBOL_GPL(nomadik_clcd_init_board);