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/backlight.h>
10 #include <linux/crc32.h>
11 #include <linux/gpio/consumer.h>
12 #include <linux/i2c.h>
13 #include <linux/module.h>
14 #include <linux/mutex.h>
16 #include <linux/regulator/consumer.h>
18 #include <drm/drm_modes.h>
19 #include <drm/drm_panel.h>
22 #include <video/videomode.h>
23 #include <video/display_timing.h>
25 #define LCD_OLINUXINO_HEADER_MAGIC 0x4F4CB727
26 #define LCD_OLINUXINO_DATA_LEN 256
28 struct lcd_olinuxino_mode
{
42 struct lcd_olinuxino_info
{
49 } __attribute__((__packed__
));
51 struct lcd_olinuxino_eeprom
{
56 struct lcd_olinuxino_info info
;
60 } __attribute__((__packed__
));
62 struct lcd_olinuxino
{
63 struct drm_panel panel
;
65 struct i2c_client
*client
;
71 struct backlight_device
*backlight
;
72 struct regulator
*supply
;
73 struct gpio_desc
*enable_gpio
;
75 struct lcd_olinuxino_eeprom eeprom
;
78 static inline struct lcd_olinuxino
*to_lcd_olinuxino(struct drm_panel
*panel
)
80 return container_of(panel
, struct lcd_olinuxino
, panel
);
83 static int lcd_olinuxino_disable(struct drm_panel
*panel
)
85 struct lcd_olinuxino
*lcd
= to_lcd_olinuxino(panel
);
90 backlight_disable(lcd
->backlight
);
97 static int lcd_olinuxino_unprepare(struct drm_panel
*panel
)
99 struct lcd_olinuxino
*lcd
= to_lcd_olinuxino(panel
);
104 gpiod_set_value_cansleep(lcd
->enable_gpio
, 0);
105 regulator_disable(lcd
->supply
);
107 lcd
->prepared
= false;
112 static int lcd_olinuxino_prepare(struct drm_panel
*panel
)
114 struct lcd_olinuxino
*lcd
= to_lcd_olinuxino(panel
);
120 ret
= regulator_enable(lcd
->supply
);
124 gpiod_set_value_cansleep(lcd
->enable_gpio
, 1);
125 lcd
->prepared
= true;
130 static int lcd_olinuxino_enable(struct drm_panel
*panel
)
132 struct lcd_olinuxino
*lcd
= to_lcd_olinuxino(panel
);
137 backlight_enable(lcd
->backlight
);
144 static int lcd_olinuxino_get_modes(struct drm_panel
*panel
)
146 struct lcd_olinuxino
*lcd
= to_lcd_olinuxino(panel
);
147 struct drm_connector
*connector
= lcd
->panel
.connector
;
148 struct lcd_olinuxino_info
*lcd_info
= &lcd
->eeprom
.info
;
149 struct drm_device
*drm
= lcd
->panel
.drm
;
150 struct lcd_olinuxino_mode
*lcd_mode
;
151 struct drm_display_mode
*mode
;
154 for (i
= 0; i
< lcd
->eeprom
.num_modes
; i
++) {
155 lcd_mode
= (struct lcd_olinuxino_mode
*)
156 &lcd
->eeprom
.reserved
[i
* sizeof(*lcd_mode
)];
158 mode
= drm_mode_create(drm
);
160 dev_err(drm
->dev
, "failed to add mode %ux%u@%u\n",
167 mode
->clock
= lcd_mode
->pixelclock
;
168 mode
->hdisplay
= lcd_mode
->hactive
;
169 mode
->hsync_start
= lcd_mode
->hactive
+ lcd_mode
->hfp
;
170 mode
->hsync_end
= lcd_mode
->hactive
+ lcd_mode
->hfp
+
172 mode
->htotal
= lcd_mode
->hactive
+ lcd_mode
->hfp
+
173 lcd_mode
->hpw
+ lcd_mode
->hbp
;
174 mode
->vdisplay
= lcd_mode
->vactive
;
175 mode
->vsync_start
= lcd_mode
->vactive
+ lcd_mode
->vfp
;
176 mode
->vsync_end
= lcd_mode
->vactive
+ lcd_mode
->vfp
+
178 mode
->vtotal
= lcd_mode
->vactive
+ lcd_mode
->vfp
+
179 lcd_mode
->vpw
+ lcd_mode
->vbp
;
180 mode
->vrefresh
= lcd_mode
->refresh
;
182 /* Always make the first mode preferred */
184 mode
->type
|= DRM_MODE_TYPE_PREFERRED
;
185 mode
->type
|= DRM_MODE_TYPE_DRIVER
;
187 drm_mode_set_name(mode
);
188 drm_mode_probed_add(connector
, mode
);
193 memcpy(connector
->display_info
.name
, lcd_info
->name
, 32);
194 connector
->display_info
.width_mm
= lcd_info
->width_mm
;
195 connector
->display_info
.height_mm
= lcd_info
->height_mm
;
196 connector
->display_info
.bpc
= lcd_info
->bpc
;
198 if (lcd_info
->bus_format
)
199 drm_display_info_set_bus_formats(&connector
->display_info
,
200 &lcd_info
->bus_format
, 1);
201 connector
->display_info
.bus_flags
= lcd_info
->bus_flag
;
206 static const struct drm_panel_funcs lcd_olinuxino_funcs
= {
207 .disable
= lcd_olinuxino_disable
,
208 .unprepare
= lcd_olinuxino_unprepare
,
209 .prepare
= lcd_olinuxino_prepare
,
210 .enable
= lcd_olinuxino_enable
,
211 .get_modes
= lcd_olinuxino_get_modes
,
214 static int lcd_olinuxino_probe(struct i2c_client
*client
,
215 const struct i2c_device_id
*id
)
217 struct device
*dev
= &client
->dev
;
218 struct lcd_olinuxino
*lcd
;
222 if (!i2c_check_functionality(client
->adapter
, I2C_FUNC_I2C
|
223 I2C_FUNC_SMBUS_READ_I2C_BLOCK
))
226 lcd
= devm_kzalloc(dev
, sizeof(*lcd
), GFP_KERNEL
);
230 i2c_set_clientdata(client
, lcd
);
232 lcd
->client
= client
;
234 mutex_init(&lcd
->mutex
);
236 /* Copy data into buffer */
237 for (i
= 0; i
< LCD_OLINUXINO_DATA_LEN
; i
+= I2C_SMBUS_BLOCK_MAX
) {
238 mutex_lock(&lcd
->mutex
);
239 ret
= i2c_smbus_read_i2c_block_data(client
,
242 (u8
*)&lcd
->eeprom
+ i
);
243 mutex_unlock(&lcd
->mutex
);
245 dev_err(dev
, "error reading from device at %02x\n", i
);
250 /* Check configuration checksum */
251 checksum
= ~crc32(~0, (u8
*)&lcd
->eeprom
, 252);
252 if (checksum
!= lcd
->eeprom
.checksum
) {
253 dev_err(dev
, "configuration checksum does not match!\n");
257 /* Check magic header */
258 if (lcd
->eeprom
.header
!= LCD_OLINUXINO_HEADER_MAGIC
) {
259 dev_err(dev
, "magic header does not match\n");
263 dev_info(dev
, "Detected %s, Rev. %s, Serial: %08x\n",
264 lcd
->eeprom
.info
.name
,
265 lcd
->eeprom
.revision
,
269 * The eeprom can hold up to 4 modes.
270 * If the stored value is bigger, overwrite it.
272 if (lcd
->eeprom
.num_modes
> 4) {
273 dev_warn(dev
, "invalid number of modes, falling back to 4\n");
274 lcd
->eeprom
.num_modes
= 4;
277 lcd
->enabled
= false;
278 lcd
->prepared
= false;
280 lcd
->supply
= devm_regulator_get(dev
, "power");
281 if (IS_ERR(lcd
->supply
))
282 return PTR_ERR(lcd
->supply
);
284 lcd
->enable_gpio
= devm_gpiod_get(dev
, "enable", GPIOD_OUT_LOW
);
285 if (IS_ERR(lcd
->enable_gpio
))
286 return PTR_ERR(lcd
->enable_gpio
);
288 lcd
->backlight
= devm_of_find_backlight(dev
);
289 if (IS_ERR(lcd
->backlight
))
290 return PTR_ERR(lcd
->backlight
);
292 drm_panel_init(&lcd
->panel
);
293 lcd
->panel
.dev
= dev
;
294 lcd
->panel
.funcs
= &lcd_olinuxino_funcs
;
296 return drm_panel_add(&lcd
->panel
);
299 static int lcd_olinuxino_remove(struct i2c_client
*client
)
301 struct lcd_olinuxino
*panel
= i2c_get_clientdata(client
);
303 drm_panel_remove(&panel
->panel
);
305 lcd_olinuxino_disable(&panel
->panel
);
306 lcd_olinuxino_unprepare(&panel
->panel
);
311 static const struct of_device_id lcd_olinuxino_of_ids
[] = {
312 { .compatible
= "olimex,lcd-olinuxino" },
315 MODULE_DEVICE_TABLE(of
, lcd_olinuxino_of_ids
);
317 static struct i2c_driver lcd_olinuxino_driver
= {
319 .name
= "lcd_olinuxino",
320 .of_match_table
= lcd_olinuxino_of_ids
,
322 .probe
= lcd_olinuxino_probe
,
323 .remove
= lcd_olinuxino_remove
,
326 module_i2c_driver(lcd_olinuxino_driver
);
328 MODULE_AUTHOR("Stefan Mavrodiev <stefan@olimex.com>");
329 MODULE_DESCRIPTION("LCD-OLinuXino driver");
330 MODULE_LICENSE("GPL");