Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetoot...
[cascardo/linux.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         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
217 int 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 EXPORT_SYMBOL_GPL(nomadik_clcd_init_panel);
235
236 #define PMU_CTRL_OFFSET 0x0000
237 #define PMU_CTRL_LCDNDIF BIT(26)
238
239 int nomadik_clcd_init_board(struct amba_device *adev,
240                             struct clcd_board *board)
241 {
242         struct regmap *pmu_regmap;
243
244         dev_info(&adev->dev, "Nomadik CLCD board init\n");
245         pmu_regmap =
246                 syscon_regmap_lookup_by_compatible("stericsson,nomadik-pmu");
247         if (IS_ERR(pmu_regmap)) {
248                 dev_err(&adev->dev, "could not find PMU syscon regmap\n");
249                 return PTR_ERR(pmu_regmap);
250         }
251         regmap_update_bits(pmu_regmap,
252                            PMU_CTRL_OFFSET,
253                            PMU_CTRL_LCDNDIF,
254                            0);
255         dev_info(&adev->dev, "set PMU mux to CLCD mode\n");
256
257         return 0;
258 }
259 EXPORT_SYMBOL_GPL(nomadik_clcd_init_board);