1
2
3
4
5
6
7
8
9
10#include <linux/module.h>
11#include <linux/delay.h>
12#include <linux/spi/spi.h>
13#include <linux/mutex.h>
14#include <linux/gpio.h>
15#include <linux/gpio/consumer.h>
16
17#include "../dss/omapdss.h"
18
19static const struct videomode lb035q02_vm = {
20 .hactive = 320,
21 .vactive = 240,
22
23 .pixelclock = 6500000,
24
25 .hsync_len = 2,
26 .hfront_porch = 20,
27 .hback_porch = 68,
28
29 .vsync_len = 2,
30 .vfront_porch = 4,
31 .vback_porch = 18,
32
33 .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
34};
35
36struct panel_drv_data {
37 struct omap_dss_device dssdev;
38
39 struct spi_device *spi;
40
41 struct videomode vm;
42
43 struct gpio_desc *enable_gpio;
44};
45
46#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
47
48static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
49{
50 struct spi_message msg;
51 struct spi_transfer index_xfer = {
52 .len = 3,
53 .cs_change = 1,
54 };
55 struct spi_transfer value_xfer = {
56 .len = 3,
57 };
58 u8 buffer[16];
59
60 spi_message_init(&msg);
61
62
63 buffer[0] = 0x70;
64 buffer[1] = 0x00;
65 buffer[2] = reg & 0x7f;
66 index_xfer.tx_buf = buffer;
67 spi_message_add_tail(&index_xfer, &msg);
68
69
70 buffer[4] = 0x72;
71 buffer[5] = val >> 8;
72 buffer[6] = val;
73 value_xfer.tx_buf = buffer + 4;
74 spi_message_add_tail(&value_xfer, &msg);
75
76 return spi_sync(spi, &msg);
77}
78
79static void init_lb035q02_panel(struct spi_device *spi)
80{
81
82 lb035q02_write_reg(spi, 0x01, 0x6300);
83 lb035q02_write_reg(spi, 0x02, 0x0200);
84 lb035q02_write_reg(spi, 0x03, 0x0177);
85 lb035q02_write_reg(spi, 0x04, 0x04c7);
86 lb035q02_write_reg(spi, 0x05, 0xffc0);
87 lb035q02_write_reg(spi, 0x06, 0xe806);
88 lb035q02_write_reg(spi, 0x0a, 0x4008);
89 lb035q02_write_reg(spi, 0x0b, 0x0000);
90 lb035q02_write_reg(spi, 0x0d, 0x0030);
91 lb035q02_write_reg(spi, 0x0e, 0x2800);
92 lb035q02_write_reg(spi, 0x0f, 0x0000);
93 lb035q02_write_reg(spi, 0x16, 0x9f80);
94 lb035q02_write_reg(spi, 0x17, 0x0a0f);
95 lb035q02_write_reg(spi, 0x1e, 0x00c1);
96 lb035q02_write_reg(spi, 0x30, 0x0300);
97 lb035q02_write_reg(spi, 0x31, 0x0007);
98 lb035q02_write_reg(spi, 0x32, 0x0000);
99 lb035q02_write_reg(spi, 0x33, 0x0000);
100 lb035q02_write_reg(spi, 0x34, 0x0707);
101 lb035q02_write_reg(spi, 0x35, 0x0004);
102 lb035q02_write_reg(spi, 0x36, 0x0302);
103 lb035q02_write_reg(spi, 0x37, 0x0202);
104 lb035q02_write_reg(spi, 0x3a, 0x0a0d);
105 lb035q02_write_reg(spi, 0x3b, 0x0806);
106}
107
108static int lb035q02_connect(struct omap_dss_device *src,
109 struct omap_dss_device *dst)
110{
111 struct panel_drv_data *ddata = to_panel_data(dst);
112
113 init_lb035q02_panel(ddata->spi);
114
115 return 0;
116}
117
118static void lb035q02_disconnect(struct omap_dss_device *src,
119 struct omap_dss_device *dst)
120{
121}
122
123static void lb035q02_enable(struct omap_dss_device *dssdev)
124{
125 struct panel_drv_data *ddata = to_panel_data(dssdev);
126
127 if (ddata->enable_gpio)
128 gpiod_set_value_cansleep(ddata->enable_gpio, 1);
129}
130
131static void lb035q02_disable(struct omap_dss_device *dssdev)
132{
133 struct panel_drv_data *ddata = to_panel_data(dssdev);
134
135 if (ddata->enable_gpio)
136 gpiod_set_value_cansleep(ddata->enable_gpio, 0);
137}
138
139static int lb035q02_get_modes(struct omap_dss_device *dssdev,
140 struct drm_connector *connector)
141{
142 struct panel_drv_data *ddata = to_panel_data(dssdev);
143
144 return omapdss_display_get_modes(connector, &ddata->vm);
145}
146
147static const struct omap_dss_device_ops lb035q02_ops = {
148 .connect = lb035q02_connect,
149 .disconnect = lb035q02_disconnect,
150
151 .enable = lb035q02_enable,
152 .disable = lb035q02_disable,
153
154 .get_modes = lb035q02_get_modes,
155};
156
157static int lb035q02_probe_of(struct spi_device *spi)
158{
159 struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
160 struct gpio_desc *gpio;
161
162 gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
163 if (IS_ERR(gpio)) {
164 dev_err(&spi->dev, "failed to parse enable gpio\n");
165 return PTR_ERR(gpio);
166 }
167
168 ddata->enable_gpio = gpio;
169
170 return 0;
171}
172
173static int lb035q02_panel_spi_probe(struct spi_device *spi)
174{
175 struct panel_drv_data *ddata;
176 struct omap_dss_device *dssdev;
177 int r;
178
179 ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
180 if (ddata == NULL)
181 return -ENOMEM;
182
183 dev_set_drvdata(&spi->dev, ddata);
184
185 ddata->spi = spi;
186
187 r = lb035q02_probe_of(spi);
188 if (r)
189 return r;
190
191 ddata->vm = lb035q02_vm;
192
193 dssdev = &ddata->dssdev;
194 dssdev->dev = &spi->dev;
195 dssdev->ops = &lb035q02_ops;
196 dssdev->type = OMAP_DISPLAY_TYPE_DPI;
197 dssdev->display = true;
198 dssdev->owner = THIS_MODULE;
199 dssdev->of_ports = BIT(0);
200 dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
201
202
203
204
205
206
207 dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH
208 | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE
209 | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
210
211 omapdss_display_init(dssdev);
212 omapdss_device_register(dssdev);
213
214 return 0;
215}
216
217static int lb035q02_panel_spi_remove(struct spi_device *spi)
218{
219 struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
220 struct omap_dss_device *dssdev = &ddata->dssdev;
221
222 omapdss_device_unregister(dssdev);
223
224 lb035q02_disable(dssdev);
225
226 return 0;
227}
228
229static const struct of_device_id lb035q02_of_match[] = {
230 { .compatible = "omapdss,lgphilips,lb035q02", },
231 {},
232};
233
234MODULE_DEVICE_TABLE(of, lb035q02_of_match);
235
236static struct spi_driver lb035q02_spi_driver = {
237 .probe = lb035q02_panel_spi_probe,
238 .remove = lb035q02_panel_spi_remove,
239 .driver = {
240 .name = "panel_lgphilips_lb035q02",
241 .of_match_table = lb035q02_of_match,
242 .suppress_bind_attrs = true,
243 },
244};
245
246module_spi_driver(lb035q02_spi_driver);
247
248MODULE_ALIAS("spi:lgphilips,lb035q02");
249MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
250MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
251MODULE_LICENSE("GPL");
252