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
;
173 mode
->vrefresh
= lcd_mode
->refresh
;
175 /* Always make the first mode preferred */
177 mode
->type
|= DRM_MODE_TYPE_PREFERRED
;
178 mode
->type
|= DRM_MODE_TYPE_DRIVER
;
180 drm_mode_set_name(mode
);
181 drm_mode_probed_add(connector
, mode
);
186 connector
->display_info
.width_mm
= lcd_info
->width_mm
;
187 connector
->display_info
.height_mm
= lcd_info
->height_mm
;
188 connector
->display_info
.bpc
= lcd_info
->bpc
;
190 if (lcd_info
->bus_format
)
191 drm_display_info_set_bus_formats(&connector
->display_info
,
192 &lcd_info
->bus_format
, 1);
193 connector
->display_info
.bus_flags
= lcd_info
->bus_flag
;
198 static const struct drm_panel_funcs lcd_olinuxino_funcs
= {
199 .disable
= lcd_olinuxino_disable
,
200 .unprepare
= lcd_olinuxino_unprepare
,
201 .prepare
= lcd_olinuxino_prepare
,
202 .enable
= lcd_olinuxino_enable
,
203 .get_modes
= lcd_olinuxino_get_modes
,
206 static int lcd_olinuxino_probe(struct i2c_client
*client
,
207 const struct i2c_device_id
*id
)
209 struct device
*dev
= &client
->dev
;
210 struct lcd_olinuxino
*lcd
;
214 if (!i2c_check_functionality(client
->adapter
, I2C_FUNC_I2C
|
215 I2C_FUNC_SMBUS_READ_I2C_BLOCK
))
218 lcd
= devm_kzalloc(dev
, sizeof(*lcd
), GFP_KERNEL
);
222 i2c_set_clientdata(client
, lcd
);
224 lcd
->client
= client
;
226 mutex_init(&lcd
->mutex
);
228 /* Copy data into buffer */
229 for (i
= 0; i
< LCD_OLINUXINO_DATA_LEN
; i
+= I2C_SMBUS_BLOCK_MAX
) {
230 mutex_lock(&lcd
->mutex
);
231 ret
= i2c_smbus_read_i2c_block_data(client
,
234 (u8
*)&lcd
->eeprom
+ i
);
235 mutex_unlock(&lcd
->mutex
);
237 dev_err(dev
, "error reading from device at %02x\n", i
);
242 /* Check configuration checksum */
243 checksum
= ~crc32(~0, (u8
*)&lcd
->eeprom
, 252);
244 if (checksum
!= lcd
->eeprom
.checksum
) {
245 dev_err(dev
, "configuration checksum does not match!\n");
249 /* Check magic header */
250 if (lcd
->eeprom
.header
!= LCD_OLINUXINO_HEADER_MAGIC
) {
251 dev_err(dev
, "magic header does not match\n");
255 dev_info(dev
, "Detected %s, Rev. %s, Serial: %08x\n",
256 lcd
->eeprom
.info
.name
,
257 lcd
->eeprom
.revision
,
261 * The eeprom can hold up to 4 modes.
262 * If the stored value is bigger, overwrite it.
264 if (lcd
->eeprom
.num_modes
> 4) {
265 dev_warn(dev
, "invalid number of modes, falling back to 4\n");
266 lcd
->eeprom
.num_modes
= 4;
269 lcd
->enabled
= false;
270 lcd
->prepared
= false;
272 lcd
->supply
= devm_regulator_get(dev
, "power");
273 if (IS_ERR(lcd
->supply
))
274 return PTR_ERR(lcd
->supply
);
276 lcd
->enable_gpio
= devm_gpiod_get(dev
, "enable", GPIOD_OUT_LOW
);
277 if (IS_ERR(lcd
->enable_gpio
))
278 return PTR_ERR(lcd
->enable_gpio
);
280 drm_panel_init(&lcd
->panel
, dev
, &lcd_olinuxino_funcs
,
281 DRM_MODE_CONNECTOR_DPI
);
283 ret
= drm_panel_of_backlight(&lcd
->panel
);
287 return drm_panel_add(&lcd
->panel
);
290 static int lcd_olinuxino_remove(struct i2c_client
*client
)
292 struct lcd_olinuxino
*panel
= i2c_get_clientdata(client
);
294 drm_panel_remove(&panel
->panel
);
296 drm_panel_disable(&panel
->panel
);
297 drm_panel_unprepare(&panel
->panel
);
302 static const struct of_device_id lcd_olinuxino_of_ids
[] = {
303 { .compatible
= "olimex,lcd-olinuxino" },
306 MODULE_DEVICE_TABLE(of
, lcd_olinuxino_of_ids
);
308 static struct i2c_driver lcd_olinuxino_driver
= {
310 .name
= "lcd_olinuxino",
311 .of_match_table
= lcd_olinuxino_of_ids
,
313 .probe
= lcd_olinuxino_probe
,
314 .remove
= lcd_olinuxino_remove
,
317 module_i2c_driver(lcd_olinuxino_driver
);
319 MODULE_AUTHOR("Stefan Mavrodiev <stefan@olimex.com>");
320 MODULE_DESCRIPTION("LCD-OLinuXino driver");
321 MODULE_LICENSE("GPL");