1 // SPDX-License-Identifier: GPL-2.0
3 * LG.Philips LB035Q02 LCD Panel Driver
5 * Copyright (C) 2019 Texas Instruments Incorporated
7 * Based on the omapdrm-specific panel-lgphilips-lb035q02 driver
9 * Copyright (C) 2013 Texas Instruments Incorporated
10 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
12 * Based on a driver by: Steve Sakoman <steve@sakoman.com>
15 #include <linux/gpio/consumer.h>
16 #include <linux/module.h>
17 #include <linux/spi/spi.h>
19 #include <drm/drm_connector.h>
20 #include <drm/drm_modes.h>
21 #include <drm/drm_panel.h>
23 struct lb035q02_device
{
24 struct drm_panel panel
;
26 struct spi_device
*spi
;
27 struct gpio_desc
*enable_gpio
;
30 #define to_lb035q02_device(p) container_of(p, struct lb035q02_device, panel)
32 static int lb035q02_write(struct lb035q02_device
*lcd
, u16 reg
, u16 val
)
34 struct spi_message msg
;
35 struct spi_transfer index_xfer
= {
39 struct spi_transfer value_xfer
= {
44 spi_message_init(&msg
);
49 buffer
[2] = reg
& 0x7f;
50 index_xfer
.tx_buf
= buffer
;
51 spi_message_add_tail(&index_xfer
, &msg
);
57 value_xfer
.tx_buf
= buffer
+ 4;
58 spi_message_add_tail(&value_xfer
, &msg
);
60 return spi_sync(lcd
->spi
, &msg
);
63 static int lb035q02_init(struct lb035q02_device
*lcd
)
65 /* Init sequence from page 28 of the lb035q02 spec. */
99 for (i
= 0; i
< ARRAY_SIZE(init_data
); ++i
) {
100 ret
= lb035q02_write(lcd
, init_data
[i
].index
,
109 static int lb035q02_disable(struct drm_panel
*panel
)
111 struct lb035q02_device
*lcd
= to_lb035q02_device(panel
);
113 gpiod_set_value_cansleep(lcd
->enable_gpio
, 0);
118 static int lb035q02_enable(struct drm_panel
*panel
)
120 struct lb035q02_device
*lcd
= to_lb035q02_device(panel
);
122 gpiod_set_value_cansleep(lcd
->enable_gpio
, 1);
127 static const struct drm_display_mode lb035q02_mode
= {
130 .hsync_start
= 320 + 20,
131 .hsync_end
= 320 + 20 + 2,
132 .htotal
= 320 + 20 + 2 + 68,
134 .vsync_start
= 240 + 4,
135 .vsync_end
= 240 + 4 + 2,
136 .vtotal
= 240 + 4 + 2 + 18,
137 .type
= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
,
138 .flags
= DRM_MODE_FLAG_NHSYNC
| DRM_MODE_FLAG_NVSYNC
,
143 static int lb035q02_get_modes(struct drm_panel
*panel
,
144 struct drm_connector
*connector
)
146 struct drm_display_mode
*mode
;
148 mode
= drm_mode_duplicate(connector
->dev
, &lb035q02_mode
);
152 drm_mode_set_name(mode
);
153 drm_mode_probed_add(connector
, mode
);
155 connector
->display_info
.width_mm
= lb035q02_mode
.width_mm
;
156 connector
->display_info
.height_mm
= lb035q02_mode
.height_mm
;
158 * FIXME: According to the datasheet pixel data is sampled on the
159 * rising edge of the clock, but the code running on the Gumstix Overo
160 * Palo35 indicates sampling on the negative edge. This should be
161 * tested on a real device.
163 connector
->display_info
.bus_flags
= DRM_BUS_FLAG_DE_HIGH
164 | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
165 | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE
;
170 static const struct drm_panel_funcs lb035q02_funcs
= {
171 .disable
= lb035q02_disable
,
172 .enable
= lb035q02_enable
,
173 .get_modes
= lb035q02_get_modes
,
176 static int lb035q02_probe(struct spi_device
*spi
)
178 struct lb035q02_device
*lcd
;
181 lcd
= devm_kzalloc(&spi
->dev
, sizeof(*lcd
), GFP_KERNEL
);
185 spi_set_drvdata(spi
, lcd
);
188 lcd
->enable_gpio
= devm_gpiod_get(&spi
->dev
, "enable", GPIOD_OUT_LOW
);
189 if (IS_ERR(lcd
->enable_gpio
)) {
190 dev_err(&spi
->dev
, "failed to parse enable gpio\n");
191 return PTR_ERR(lcd
->enable_gpio
);
194 ret
= lb035q02_init(lcd
);
198 drm_panel_init(&lcd
->panel
, &lcd
->spi
->dev
, &lb035q02_funcs
,
199 DRM_MODE_CONNECTOR_DPI
);
201 drm_panel_add(&lcd
->panel
);
206 static int lb035q02_remove(struct spi_device
*spi
)
208 struct lb035q02_device
*lcd
= spi_get_drvdata(spi
);
210 drm_panel_remove(&lcd
->panel
);
211 drm_panel_disable(&lcd
->panel
);
216 static const struct of_device_id lb035q02_of_match
[] = {
217 { .compatible
= "lgphilips,lb035q02", },
221 MODULE_DEVICE_TABLE(of
, lb035q02_of_match
);
223 static const struct spi_device_id lb035q02_ids
[] = {
228 MODULE_DEVICE_TABLE(spi
, lb035q02_ids
);
230 static struct spi_driver lb035q02_driver
= {
231 .probe
= lb035q02_probe
,
232 .remove
= lb035q02_remove
,
233 .id_table
= lb035q02_ids
,
235 .name
= "panel-lg-lb035q02",
236 .of_match_table
= lb035q02_of_match
,
240 module_spi_driver(lb035q02_driver
);
242 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
243 MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
244 MODULE_LICENSE("GPL");