1 // SPDX-License-Identifier: GPL-2.0+
3 * LCD-OLinuXino support for panel driver
5 * Copyright (C) 2018 Olimex Ltd.
6 * Author: Stefan Mavrodiev <stefan@olimex.com>
9 #include <linux/crc32.h>
10 #include <linux/gpio/consumer.h>
11 #include <linux/i2c.h>
12 #include <linux/module.h>
13 #include <linux/mutex.h>
15 #include <linux/regulator/consumer.h>
17 #include <video/videomode.h>
18 #include <video/display_timing.h>
20 #include <drm/drm_device.h>
21 #include <drm/drm_modes.h>
22 #include <drm/drm_panel.h>
24 #define LCD_OLINUXINO_HEADER_MAGIC 0x4F4CB727
25 #define LCD_OLINUXINO_DATA_LEN 256
27 struct lcd_olinuxino_mode
{
41 struct lcd_olinuxino_info
{
48 } __attribute__((__packed__
));
50 struct lcd_olinuxino_eeprom
{
55 struct lcd_olinuxino_info info
;
59 } __attribute__((__packed__
));
61 struct lcd_olinuxino
{
62 struct drm_panel panel
;
64 struct i2c_client
*client
;
70 struct regulator
*supply
;
71 struct gpio_desc
*enable_gpio
;
73 struct lcd_olinuxino_eeprom eeprom
;
76 static inline struct lcd_olinuxino
*to_lcd_olinuxino(struct drm_panel
*panel
)
78 return container_of(panel
, struct lcd_olinuxino
, panel
);
81 static int lcd_olinuxino_disable(struct drm_panel
*panel
)
83 struct lcd_olinuxino
*lcd
= to_lcd_olinuxino(panel
);
93 static int lcd_olinuxino_unprepare(struct drm_panel
*panel
)
95 struct lcd_olinuxino
*lcd
= to_lcd_olinuxino(panel
);
100 gpiod_set_value_cansleep(lcd
->enable_gpio
, 0);
101 regulator_disable(lcd
->supply
);
103 lcd
->prepared
= false;
108 static int lcd_olinuxino_prepare(struct drm_panel
*panel
)
110 struct lcd_olinuxino
*lcd
= to_lcd_olinuxino(panel
);
116 ret
= regulator_enable(lcd
->supply
);
120 gpiod_set_value_cansleep(lcd
->enable_gpio
, 1);
121 lcd
->prepared
= true;
126 static int lcd_olinuxino_enable(struct drm_panel
*panel
)
128 struct lcd_olinuxino
*lcd
= to_lcd_olinuxino(panel
);
138 static int lcd_olinuxino_get_modes(struct drm_panel
*panel
,
139 struct drm_connector
*connector
)
141 struct lcd_olinuxino
*lcd
= to_lcd_olinuxino(panel
);
142 struct lcd_olinuxino_info
*lcd_info
= &lcd
->eeprom
.info
;
143 struct lcd_olinuxino_mode
*lcd_mode
;
144 struct drm_display_mode
*mode
;
147 for (i
= 0; i
< lcd
->eeprom
.num_modes
; i
++) {
148 lcd_mode
= (struct lcd_olinuxino_mode
*)
149 &lcd
->eeprom
.reserved
[i
* sizeof(*lcd_mode
)];
151 mode
= drm_mode_create(connector
->dev
);
153 dev_err(panel
->dev
, "failed to add mode %ux%u@%u\n",
160 mode
->clock
= lcd_mode
->pixelclock
;
161 mode
->hdisplay
= lcd_mode
->hactive
;
162 mode
->hsync_start
= lcd_mode
->hactive
+ lcd_mode
->hfp
;
163 mode
->hsync_end
= lcd_mode
->hactive
+ lcd_mode
->hfp
+
165 mode
->htotal
= lcd_mode
->hactive
+ lcd_mode
->hfp
+
166 lcd_mode
->hpw
+ lcd_mode
->hbp
;
167 mode
->vdisplay
= lcd_mode
->vactive
;
168 mode
->vsync_start
= lcd_mode
->vactive
+ lcd_mode
->vfp
;
169 mode
->vsync_end
= lcd_mode
->vactive
+ lcd_mode
->vfp
+
171 mode
->vtotal
= lcd_mode
->vactive
+ lcd_mode
->vfp
+
172 lcd_mode
->vpw
+ lcd_mode
->vbp
;
174 /* Always make the first mode preferred */
176 mode
->type
|= DRM_MODE_TYPE_PREFERRED
;
177 mode
->type
|= DRM_MODE_TYPE_DRIVER
;
179 drm_mode_set_name(mode
);
180 drm_mode_probed_add(connector
, mode
);
185 connector
->display_info
.width_mm
= lcd_info
->width_mm
;
186 connector
->display_info
.height_mm
= lcd_info
->height_mm
;
187 connector
->display_info
.bpc
= lcd_info
->bpc
;
189 if (lcd_info
->bus_format
)
190 drm_display_info_set_bus_formats(&connector
->display_info
,
191 &lcd_info
->bus_format
, 1);
192 connector
->display_info
.bus_flags
= lcd_info
->bus_flag
;
197 static const struct drm_panel_funcs lcd_olinuxino_funcs
= {
198 .disable
= lcd_olinuxino_disable
,
199 .unprepare
= lcd_olinuxino_unprepare
,
200 .prepare
= lcd_olinuxino_prepare
,
201 .enable
= lcd_olinuxino_enable
,
202 .get_modes
= lcd_olinuxino_get_modes
,
205 static int lcd_olinuxino_probe(struct i2c_client
*client
,
206 const struct i2c_device_id
*id
)
208 struct device
*dev
= &client
->dev
;
209 struct lcd_olinuxino
*lcd
;
213 if (!i2c_check_functionality(client
->adapter
, I2C_FUNC_I2C
|
214 I2C_FUNC_SMBUS_READ_I2C_BLOCK
))
217 lcd
= devm_kzalloc(dev
, sizeof(*lcd
), GFP_KERNEL
);
221 i2c_set_clientdata(client
, lcd
);
223 lcd
->client
= client
;
225 mutex_init(&lcd
->mutex
);
227 /* Copy data into buffer */
228 for (i
= 0; i
< LCD_OLINUXINO_DATA_LEN
; i
+= I2C_SMBUS_BLOCK_MAX
) {
229 mutex_lock(&lcd
->mutex
);
230 ret
= i2c_smbus_read_i2c_block_data(client
,
233 (u8
*)&lcd
->eeprom
+ i
);
234 mutex_unlock(&lcd
->mutex
);
236 dev_err(dev
, "error reading from device at %02x\n", i
);
241 /* Check configuration checksum */
242 checksum
= ~crc32(~0, (u8
*)&lcd
->eeprom
, 252);
243 if (checksum
!= lcd
->eeprom
.checksum
) {
244 dev_err(dev
, "configuration checksum does not match!\n");
248 /* Check magic header */
249 if (lcd
->eeprom
.header
!= LCD_OLINUXINO_HEADER_MAGIC
) {
250 dev_err(dev
, "magic header does not match\n");
254 dev_info(dev
, "Detected %s, Rev. %s, Serial: %08x\n",
255 lcd
->eeprom
.info
.name
,
256 lcd
->eeprom
.revision
,
260 * The eeprom can hold up to 4 modes.
261 * If the stored value is bigger, overwrite it.
263 if (lcd
->eeprom
.num_modes
> 4) {
264 dev_warn(dev
, "invalid number of modes, falling back to 4\n");
265 lcd
->eeprom
.num_modes
= 4;
268 lcd
->enabled
= false;
269 lcd
->prepared
= false;
271 lcd
->supply
= devm_regulator_get(dev
, "power");
272 if (IS_ERR(lcd
->supply
))
273 return PTR_ERR(lcd
->supply
);
275 lcd
->enable_gpio
= devm_gpiod_get(dev
, "enable", GPIOD_OUT_LOW
);
276 if (IS_ERR(lcd
->enable_gpio
))
277 return PTR_ERR(lcd
->enable_gpio
);
279 drm_panel_init(&lcd
->panel
, dev
, &lcd_olinuxino_funcs
,
280 DRM_MODE_CONNECTOR_DPI
);
282 ret
= drm_panel_of_backlight(&lcd
->panel
);
286 drm_panel_add(&lcd
->panel
);
291 static int lcd_olinuxino_remove(struct i2c_client
*client
)
293 struct lcd_olinuxino
*panel
= i2c_get_clientdata(client
);
295 drm_panel_remove(&panel
->panel
);
297 drm_panel_disable(&panel
->panel
);
298 drm_panel_unprepare(&panel
->panel
);
303 static const struct of_device_id lcd_olinuxino_of_ids
[] = {
304 { .compatible
= "olimex,lcd-olinuxino" },
307 MODULE_DEVICE_TABLE(of
, lcd_olinuxino_of_ids
);
309 static struct i2c_driver lcd_olinuxino_driver
= {
311 .name
= "lcd_olinuxino",
312 .of_match_table
= lcd_olinuxino_of_ids
,
314 .probe
= lcd_olinuxino_probe
,
315 .remove
= lcd_olinuxino_remove
,
318 module_i2c_driver(lcd_olinuxino_driver
);
320 MODULE_AUTHOR("Stefan Mavrodiev <stefan@olimex.com>");
321 MODULE_DESCRIPTION("LCD-OLinuXino driver");
322 MODULE_LICENSE("GPL");