1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * l4f00242t03.c -- support for Epson L4F00242T03 LCD |
4 | * |
5 | * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved. |
6 | * |
7 | * Copyright (c) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com> |
8 | * Inspired by Marek Vasut work in l4f00242t03.c |
9 | */ |
10 | |
11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
12 | |
13 | #include <linux/device.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/module.h> |
17 | #include <linux/gpio/consumer.h> |
18 | #include <linux/lcd.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/regulator/consumer.h> |
21 | #include <linux/spi/spi.h> |
22 | |
23 | struct l4f00242t03_priv { |
24 | struct spi_device *spi; |
25 | struct lcd_device *ld; |
26 | int lcd_state; |
27 | struct regulator *io_reg; |
28 | struct regulator *core_reg; |
29 | struct gpio_desc *reset; |
30 | struct gpio_desc *enable; |
31 | }; |
32 | |
33 | static void l4f00242t03_reset(struct gpio_desc *gpiod) |
34 | { |
35 | pr_debug("l4f00242t03_reset.\n" ); |
36 | gpiod_set_value(desc: gpiod, value: 1); |
37 | mdelay(100); |
38 | gpiod_set_value(desc: gpiod, value: 0); |
39 | mdelay(10); /* tRES >= 100us */ |
40 | gpiod_set_value(desc: gpiod, value: 1); |
41 | mdelay(20); |
42 | } |
43 | |
44 | #define param(x) ((x) | 0x100) |
45 | |
46 | static void l4f00242t03_lcd_init(struct spi_device *spi) |
47 | { |
48 | struct l4f00242t03_priv *priv = spi_get_drvdata(spi); |
49 | const u16 cmd[] = { 0x36, param(0), 0x3A, param(0x60) }; |
50 | int ret; |
51 | |
52 | dev_dbg(&spi->dev, "initializing LCD\n" ); |
53 | |
54 | ret = regulator_set_voltage(regulator: priv->io_reg, min_uV: 1800000, max_uV: 1800000); |
55 | if (ret) { |
56 | dev_err(&spi->dev, "failed to set the IO regulator voltage.\n" ); |
57 | return; |
58 | } |
59 | ret = regulator_enable(regulator: priv->io_reg); |
60 | if (ret) { |
61 | dev_err(&spi->dev, "failed to enable the IO regulator.\n" ); |
62 | return; |
63 | } |
64 | |
65 | ret = regulator_set_voltage(regulator: priv->core_reg, min_uV: 2800000, max_uV: 2800000); |
66 | if (ret) { |
67 | dev_err(&spi->dev, "failed to set the core regulator voltage.\n" ); |
68 | regulator_disable(regulator: priv->io_reg); |
69 | return; |
70 | } |
71 | ret = regulator_enable(regulator: priv->core_reg); |
72 | if (ret) { |
73 | dev_err(&spi->dev, "failed to enable the core regulator.\n" ); |
74 | regulator_disable(regulator: priv->io_reg); |
75 | return; |
76 | } |
77 | |
78 | l4f00242t03_reset(gpiod: priv->reset); |
79 | |
80 | gpiod_set_value(desc: priv->enable, value: 1); |
81 | msleep(msecs: 60); |
82 | spi_write(spi, buf: (const u8 *)cmd, ARRAY_SIZE(cmd) * sizeof(u16)); |
83 | } |
84 | |
85 | static void l4f00242t03_lcd_powerdown(struct spi_device *spi) |
86 | { |
87 | struct l4f00242t03_priv *priv = spi_get_drvdata(spi); |
88 | |
89 | dev_dbg(&spi->dev, "Powering down LCD\n" ); |
90 | |
91 | gpiod_set_value(desc: priv->enable, value: 0); |
92 | |
93 | regulator_disable(regulator: priv->io_reg); |
94 | regulator_disable(regulator: priv->core_reg); |
95 | } |
96 | |
97 | static int l4f00242t03_lcd_power_get(struct lcd_device *ld) |
98 | { |
99 | struct l4f00242t03_priv *priv = lcd_get_data(ld_dev: ld); |
100 | |
101 | return priv->lcd_state; |
102 | } |
103 | |
104 | static int l4f00242t03_lcd_power_set(struct lcd_device *ld, int power) |
105 | { |
106 | struct l4f00242t03_priv *priv = lcd_get_data(ld_dev: ld); |
107 | struct spi_device *spi = priv->spi; |
108 | |
109 | const u16 slpout = 0x11; |
110 | const u16 dison = 0x29; |
111 | |
112 | const u16 slpin = 0x10; |
113 | const u16 disoff = 0x28; |
114 | |
115 | if (power <= FB_BLANK_NORMAL) { |
116 | if (priv->lcd_state <= FB_BLANK_NORMAL) { |
117 | /* Do nothing, the LCD is running */ |
118 | } else if (priv->lcd_state < FB_BLANK_POWERDOWN) { |
119 | dev_dbg(&spi->dev, "Resuming LCD\n" ); |
120 | |
121 | spi_write(spi, buf: (const u8 *)&slpout, len: sizeof(u16)); |
122 | msleep(msecs: 60); |
123 | spi_write(spi, buf: (const u8 *)&dison, len: sizeof(u16)); |
124 | } else { |
125 | /* priv->lcd_state == FB_BLANK_POWERDOWN */ |
126 | l4f00242t03_lcd_init(spi); |
127 | priv->lcd_state = FB_BLANK_VSYNC_SUSPEND; |
128 | l4f00242t03_lcd_power_set(ld: priv->ld, power); |
129 | } |
130 | } else if (power < FB_BLANK_POWERDOWN) { |
131 | if (priv->lcd_state <= FB_BLANK_NORMAL) { |
132 | /* Send the display in standby */ |
133 | dev_dbg(&spi->dev, "Standby the LCD\n" ); |
134 | |
135 | spi_write(spi, buf: (const u8 *)&disoff, len: sizeof(u16)); |
136 | msleep(msecs: 60); |
137 | spi_write(spi, buf: (const u8 *)&slpin, len: sizeof(u16)); |
138 | } else if (priv->lcd_state < FB_BLANK_POWERDOWN) { |
139 | /* Do nothing, the LCD is already in standby */ |
140 | } else { |
141 | /* priv->lcd_state == FB_BLANK_POWERDOWN */ |
142 | l4f00242t03_lcd_init(spi); |
143 | priv->lcd_state = FB_BLANK_UNBLANK; |
144 | l4f00242t03_lcd_power_set(ld, power); |
145 | } |
146 | } else { |
147 | /* power == FB_BLANK_POWERDOWN */ |
148 | if (priv->lcd_state != FB_BLANK_POWERDOWN) { |
149 | /* Clear the screen before shutting down */ |
150 | spi_write(spi, buf: (const u8 *)&disoff, len: sizeof(u16)); |
151 | msleep(msecs: 60); |
152 | l4f00242t03_lcd_powerdown(spi); |
153 | } |
154 | } |
155 | |
156 | priv->lcd_state = power; |
157 | |
158 | return 0; |
159 | } |
160 | |
161 | static struct lcd_ops l4f_ops = { |
162 | .set_power = l4f00242t03_lcd_power_set, |
163 | .get_power = l4f00242t03_lcd_power_get, |
164 | }; |
165 | |
166 | static int l4f00242t03_probe(struct spi_device *spi) |
167 | { |
168 | struct l4f00242t03_priv *priv; |
169 | |
170 | priv = devm_kzalloc(dev: &spi->dev, size: sizeof(struct l4f00242t03_priv), |
171 | GFP_KERNEL); |
172 | if (priv == NULL) |
173 | return -ENOMEM; |
174 | |
175 | spi_set_drvdata(spi, data: priv); |
176 | spi->bits_per_word = 9; |
177 | spi_setup(spi); |
178 | |
179 | priv->spi = spi; |
180 | |
181 | priv->reset = devm_gpiod_get(dev: &spi->dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
182 | if (IS_ERR(ptr: priv->reset)) |
183 | return dev_err_probe(dev: &spi->dev, err: PTR_ERR(ptr: priv->reset), |
184 | fmt: "Unable to get the lcd l4f00242t03 reset gpio.\n" ); |
185 | gpiod_set_consumer_name(desc: priv->reset, name: "lcd l4f00242t03 reset" ); |
186 | |
187 | priv->enable = devm_gpiod_get(dev: &spi->dev, con_id: "enable" , flags: GPIOD_OUT_LOW); |
188 | if (IS_ERR(ptr: priv->enable)) |
189 | return dev_err_probe(dev: &spi->dev, err: PTR_ERR(ptr: priv->enable), |
190 | fmt: "Unable to get the lcd l4f00242t03 data en gpio.\n" ); |
191 | gpiod_set_consumer_name(desc: priv->enable, name: "lcd l4f00242t03 data enable" ); |
192 | |
193 | priv->io_reg = devm_regulator_get(dev: &spi->dev, id: "vdd" ); |
194 | if (IS_ERR(ptr: priv->io_reg)) |
195 | return dev_err_probe(dev: &spi->dev, err: PTR_ERR(ptr: priv->io_reg), |
196 | fmt: "%s: Unable to get the IO regulator\n" , |
197 | __func__); |
198 | |
199 | priv->core_reg = devm_regulator_get(dev: &spi->dev, id: "vcore" ); |
200 | if (IS_ERR(ptr: priv->core_reg)) |
201 | return dev_err_probe(dev: &spi->dev, err: PTR_ERR(ptr: priv->core_reg), |
202 | fmt: "%s: Unable to get the core regulator\n" , |
203 | __func__); |
204 | |
205 | priv->ld = devm_lcd_device_register(dev: &spi->dev, name: "l4f00242t03" , parent: &spi->dev, |
206 | devdata: priv, ops: &l4f_ops); |
207 | if (IS_ERR(ptr: priv->ld)) |
208 | return PTR_ERR(ptr: priv->ld); |
209 | |
210 | /* Init the LCD */ |
211 | l4f00242t03_lcd_init(spi); |
212 | priv->lcd_state = FB_BLANK_VSYNC_SUSPEND; |
213 | l4f00242t03_lcd_power_set(ld: priv->ld, power: FB_BLANK_UNBLANK); |
214 | |
215 | dev_info(&spi->dev, "Epson l4f00242t03 lcd probed.\n" ); |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | static void l4f00242t03_remove(struct spi_device *spi) |
221 | { |
222 | struct l4f00242t03_priv *priv = spi_get_drvdata(spi); |
223 | |
224 | l4f00242t03_lcd_power_set(ld: priv->ld, power: FB_BLANK_POWERDOWN); |
225 | } |
226 | |
227 | static void l4f00242t03_shutdown(struct spi_device *spi) |
228 | { |
229 | struct l4f00242t03_priv *priv = spi_get_drvdata(spi); |
230 | |
231 | if (priv) |
232 | l4f00242t03_lcd_power_set(ld: priv->ld, power: FB_BLANK_POWERDOWN); |
233 | |
234 | } |
235 | |
236 | static struct spi_driver l4f00242t03_driver = { |
237 | .driver = { |
238 | .name = "l4f00242t03" , |
239 | }, |
240 | .probe = l4f00242t03_probe, |
241 | .remove = l4f00242t03_remove, |
242 | .shutdown = l4f00242t03_shutdown, |
243 | }; |
244 | |
245 | module_spi_driver(l4f00242t03_driver); |
246 | |
247 | MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>" ); |
248 | MODULE_DESCRIPTION("EPSON L4F00242T03 LCD" ); |
249 | MODULE_LICENSE("GPL v2" ); |
250 | |