Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux-2.6-microblaze.git] / drivers / video / backlight / tosa_lcd.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  LCD / Backlight control code for Sharp SL-6000x (tosa)
4  *
5  *  Copyright (c) 2005          Dirk Opfer
6  *  Copyright (c) 2007,2008     Dmitry Baryshkov
7  */
8
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/device.h>
12 #include <linux/spi/spi.h>
13 #include <linux/i2c.h>
14 #include <linux/slab.h>
15 #include <linux/gpio/consumer.h>
16 #include <linux/delay.h>
17 #include <linux/lcd.h>
18 #include <linux/fb.h>
19
20 #include <asm/mach/sharpsl_param.h>
21
22 #include "tosa_bl.h"
23
24 #define POWER_IS_ON(pwr)        ((pwr) <= FB_BLANK_NORMAL)
25
26 #define TG_REG0_VQV     0x0001
27 #define TG_REG0_COLOR   0x0002
28 #define TG_REG0_UD      0x0004
29 #define TG_REG0_LR      0x0008
30
31 /*
32  * Timing Generator
33  */
34 #define TG_PNLCTL       0x00
35 #define TG_TPOSCTL      0x01
36 #define TG_DUTYCTL      0x02
37 #define TG_GPOSR        0x03
38 #define TG_GPODR1       0x04
39 #define TG_GPODR2       0x05
40 #define TG_PINICTL      0x06
41 #define TG_HPOSCTL      0x07
42
43
44 #define DAC_BASE        0x4e
45
46 struct tosa_lcd_data {
47         struct spi_device *spi;
48         struct lcd_device *lcd;
49         struct i2c_client *i2c;
50         struct gpio_desc *gpiod_tg;
51
52         int lcd_power;
53         bool is_vga;
54 };
55
56 static int tosa_tg_send(struct spi_device *spi, int adrs, uint8_t data)
57 {
58         u8 buf[1];
59         struct spi_message msg;
60         struct spi_transfer xfer = {
61                 .len            = 1,
62                 .cs_change      = 0,
63                 .tx_buf         = buf,
64         };
65
66         buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f);
67         spi_message_init(&msg);
68         spi_message_add_tail(&xfer, &msg);
69
70         return spi_sync(spi, &msg);
71 }
72
73 int tosa_bl_enable(struct spi_device *spi, int enable)
74 {
75         /* bl_enable GP04=1 otherwise GP04=0*/
76         return tosa_tg_send(spi, TG_GPODR2, enable ? 0x01 : 0x00);
77 }
78 EXPORT_SYMBOL(tosa_bl_enable);
79
80 static void tosa_lcd_tg_init(struct tosa_lcd_data *data)
81 {
82         /* TG on */
83         gpiod_set_value(data->gpiod_tg, 0);
84
85         mdelay(60);
86
87         /* delayed 0clk TCTL signal for VGA */
88         tosa_tg_send(data->spi, TG_TPOSCTL, 0x00);
89         /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
90         tosa_tg_send(data->spi, TG_GPOSR, 0x02);
91 }
92
93 static void tosa_lcd_tg_on(struct tosa_lcd_data *data)
94 {
95         struct spi_device *spi = data->spi;
96         int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
97
98         if (data->is_vga)
99                 value |= TG_REG0_VQV;
100
101         tosa_tg_send(spi, TG_PNLCTL, value);
102
103         /* TG LCD pannel power up */
104         tosa_tg_send(spi, TG_PINICTL, 0x4);
105         mdelay(50);
106
107         /* TG LCD GVSS */
108         tosa_tg_send(spi, TG_PINICTL, 0x0);
109
110         if (IS_ERR_OR_NULL(data->i2c)) {
111                 /*
112                  * after the pannel is powered up the first time,
113                  * we can access the i2c bus so probe for the DAC
114                  */
115                 struct i2c_adapter *adap = i2c_get_adapter(0);
116                 struct i2c_board_info info = {
117                         .dev_name = "tosa-bl",
118                         .type   = "tosa-bl",
119                         .addr   = DAC_BASE,
120                         .platform_data = data->spi,
121                 };
122                 data->i2c = i2c_new_client_device(adap, &info);
123         }
124 }
125
126 static void tosa_lcd_tg_off(struct tosa_lcd_data *data)
127 {
128         struct spi_device *spi = data->spi;
129
130         /* TG LCD VHSA off */
131         tosa_tg_send(spi, TG_PINICTL, 0x4);
132         mdelay(50);
133
134         /* TG LCD signal off */
135         tosa_tg_send(spi, TG_PINICTL, 0x6);
136         mdelay(50);
137
138         /* TG Off */
139         gpiod_set_value(data->gpiod_tg, 1);
140         mdelay(100);
141 }
142
143 int tosa_lcd_set_power(struct lcd_device *lcd, int power)
144 {
145         struct tosa_lcd_data *data = lcd_get_data(lcd);
146
147         if (POWER_IS_ON(power) && !POWER_IS_ON(data->lcd_power))
148                 tosa_lcd_tg_on(data);
149
150         if (!POWER_IS_ON(power) && POWER_IS_ON(data->lcd_power))
151                 tosa_lcd_tg_off(data);
152
153         data->lcd_power = power;
154         return 0;
155 }
156
157 static int tosa_lcd_get_power(struct lcd_device *lcd)
158 {
159         struct tosa_lcd_data *data = lcd_get_data(lcd);
160
161         return data->lcd_power;
162 }
163
164 static int tosa_lcd_set_mode(struct lcd_device *lcd, struct fb_videomode *mode)
165 {
166         struct tosa_lcd_data *data = lcd_get_data(lcd);
167
168         if (mode->xres == 320 || mode->yres == 320)
169                 data->is_vga = false;
170         else
171                 data->is_vga = true;
172
173         if (POWER_IS_ON(data->lcd_power))
174                 tosa_lcd_tg_on(data);
175
176         return 0;
177 }
178
179 static struct lcd_ops tosa_lcd_ops = {
180         .set_power = tosa_lcd_set_power,
181         .get_power = tosa_lcd_get_power,
182         .set_mode = tosa_lcd_set_mode,
183 };
184
185 static int tosa_lcd_probe(struct spi_device *spi)
186 {
187         int ret;
188         struct tosa_lcd_data *data;
189
190         data = devm_kzalloc(&spi->dev, sizeof(struct tosa_lcd_data),
191                                 GFP_KERNEL);
192         if (!data)
193                 return -ENOMEM;
194
195         data->is_vga = true; /* default to VGA mode */
196
197         /*
198          * bits_per_word cannot be configured in platform data
199          */
200         spi->bits_per_word = 8;
201
202         ret = spi_setup(spi);
203         if (ret < 0)
204                 return ret;
205
206         data->spi = spi;
207         spi_set_drvdata(spi, data);
208
209         data->gpiod_tg = devm_gpiod_get(&spi->dev, "tg #pwr", GPIOD_OUT_LOW);
210         if (IS_ERR(data->gpiod_tg))
211                 return PTR_ERR(data->gpiod_tg);
212
213         mdelay(60);
214
215         tosa_lcd_tg_init(data);
216
217         tosa_lcd_tg_on(data);
218
219         data->lcd = devm_lcd_device_register(&spi->dev, "tosa-lcd", &spi->dev,
220                                         data, &tosa_lcd_ops);
221
222         if (IS_ERR(data->lcd)) {
223                 ret = PTR_ERR(data->lcd);
224                 data->lcd = NULL;
225                 goto err_register;
226         }
227
228         return 0;
229
230 err_register:
231         tosa_lcd_tg_off(data);
232         return ret;
233 }
234
235 static int tosa_lcd_remove(struct spi_device *spi)
236 {
237         struct tosa_lcd_data *data = spi_get_drvdata(spi);
238
239         i2c_unregister_device(data->i2c);
240
241         tosa_lcd_tg_off(data);
242
243         return 0;
244 }
245
246 #ifdef CONFIG_PM_SLEEP
247 static int tosa_lcd_suspend(struct device *dev)
248 {
249         struct tosa_lcd_data *data = dev_get_drvdata(dev);
250
251         tosa_lcd_tg_off(data);
252
253         return 0;
254 }
255
256 static int tosa_lcd_resume(struct device *dev)
257 {
258         struct tosa_lcd_data *data = dev_get_drvdata(dev);
259
260         tosa_lcd_tg_init(data);
261         if (POWER_IS_ON(data->lcd_power))
262                 tosa_lcd_tg_on(data);
263         else
264                 tosa_lcd_tg_off(data);
265
266         return 0;
267 }
268 #endif
269
270 static SIMPLE_DEV_PM_OPS(tosa_lcd_pm_ops, tosa_lcd_suspend, tosa_lcd_resume);
271
272 static struct spi_driver tosa_lcd_driver = {
273         .driver = {
274                 .name           = "tosa-lcd",
275                 .pm             = &tosa_lcd_pm_ops,
276         },
277         .probe          = tosa_lcd_probe,
278         .remove         = tosa_lcd_remove,
279 };
280
281 module_spi_driver(tosa_lcd_driver);
282
283 MODULE_AUTHOR("Dmitry Baryshkov");
284 MODULE_LICENSE("GPL v2");
285 MODULE_DESCRIPTION("LCD/Backlight control for Sharp SL-6000 PDA");
286 MODULE_ALIAS("spi:tosa-lcd");