xref: /openbmc/linux/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c (revision 0791faebfe750292a8a842b64795a390ca4a3b51)
117fd7a9dSStefan Mavrodiev // SPDX-License-Identifier: GPL-2.0+
217fd7a9dSStefan Mavrodiev /*
317fd7a9dSStefan Mavrodiev  * LCD-OLinuXino support for panel driver
417fd7a9dSStefan Mavrodiev  *
517fd7a9dSStefan Mavrodiev  * Copyright (C) 2018 Olimex Ltd.
617fd7a9dSStefan Mavrodiev  *   Author: Stefan Mavrodiev <stefan@olimex.com>
717fd7a9dSStefan Mavrodiev  */
817fd7a9dSStefan Mavrodiev 
917fd7a9dSStefan Mavrodiev #include <linux/crc32.h>
1017fd7a9dSStefan Mavrodiev #include <linux/gpio/consumer.h>
1117fd7a9dSStefan Mavrodiev #include <linux/i2c.h>
1217fd7a9dSStefan Mavrodiev #include <linux/module.h>
1317fd7a9dSStefan Mavrodiev #include <linux/mutex.h>
1417fd7a9dSStefan Mavrodiev #include <linux/of.h>
1517fd7a9dSStefan Mavrodiev #include <linux/regulator/consumer.h>
1617fd7a9dSStefan Mavrodiev 
1717fd7a9dSStefan Mavrodiev #include <video/videomode.h>
1817fd7a9dSStefan Mavrodiev #include <video/display_timing.h>
1917fd7a9dSStefan Mavrodiev 
20cb23eae3SSam Ravnborg #include <drm/drm_device.h>
21cb23eae3SSam Ravnborg #include <drm/drm_modes.h>
22cb23eae3SSam Ravnborg #include <drm/drm_panel.h>
23cb23eae3SSam Ravnborg 
2417fd7a9dSStefan Mavrodiev #define LCD_OLINUXINO_HEADER_MAGIC	0x4F4CB727
2517fd7a9dSStefan Mavrodiev #define LCD_OLINUXINO_DATA_LEN		256
2617fd7a9dSStefan Mavrodiev 
2717fd7a9dSStefan Mavrodiev struct lcd_olinuxino_mode {
2817fd7a9dSStefan Mavrodiev 	u32 pixelclock;
2917fd7a9dSStefan Mavrodiev 	u32 hactive;
3017fd7a9dSStefan Mavrodiev 	u32 hfp;
3117fd7a9dSStefan Mavrodiev 	u32 hbp;
3217fd7a9dSStefan Mavrodiev 	u32 hpw;
3317fd7a9dSStefan Mavrodiev 	u32 vactive;
3417fd7a9dSStefan Mavrodiev 	u32 vfp;
3517fd7a9dSStefan Mavrodiev 	u32 vbp;
3617fd7a9dSStefan Mavrodiev 	u32 vpw;
3717fd7a9dSStefan Mavrodiev 	u32 refresh;
3817fd7a9dSStefan Mavrodiev 	u32 flags;
3917fd7a9dSStefan Mavrodiev };
4017fd7a9dSStefan Mavrodiev 
4117fd7a9dSStefan Mavrodiev struct lcd_olinuxino_info {
4217fd7a9dSStefan Mavrodiev 	char name[32];
4317fd7a9dSStefan Mavrodiev 	u32 width_mm;
4417fd7a9dSStefan Mavrodiev 	u32 height_mm;
4517fd7a9dSStefan Mavrodiev 	u32 bpc;
4617fd7a9dSStefan Mavrodiev 	u32 bus_format;
4717fd7a9dSStefan Mavrodiev 	u32 bus_flag;
4817fd7a9dSStefan Mavrodiev } __attribute__((__packed__));
4917fd7a9dSStefan Mavrodiev 
5017fd7a9dSStefan Mavrodiev struct lcd_olinuxino_eeprom {
5117fd7a9dSStefan Mavrodiev 	u32 header;
5217fd7a9dSStefan Mavrodiev 	u32 id;
5317fd7a9dSStefan Mavrodiev 	char revision[4];
5417fd7a9dSStefan Mavrodiev 	u32 serial;
5517fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino_info info;
5617fd7a9dSStefan Mavrodiev 	u32 num_modes;
5717fd7a9dSStefan Mavrodiev 	u8 reserved[180];
5817fd7a9dSStefan Mavrodiev 	u32 checksum;
5917fd7a9dSStefan Mavrodiev } __attribute__((__packed__));
6017fd7a9dSStefan Mavrodiev 
6117fd7a9dSStefan Mavrodiev struct lcd_olinuxino {
6217fd7a9dSStefan Mavrodiev 	struct drm_panel panel;
6317fd7a9dSStefan Mavrodiev 	struct device *dev;
6417fd7a9dSStefan Mavrodiev 	struct i2c_client *client;
6517fd7a9dSStefan Mavrodiev 	struct mutex mutex;
6617fd7a9dSStefan Mavrodiev 
6717fd7a9dSStefan Mavrodiev 	bool prepared;
6817fd7a9dSStefan Mavrodiev 	bool enabled;
6917fd7a9dSStefan Mavrodiev 
7017fd7a9dSStefan Mavrodiev 	struct regulator *supply;
7117fd7a9dSStefan Mavrodiev 	struct gpio_desc *enable_gpio;
7217fd7a9dSStefan Mavrodiev 
7317fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino_eeprom eeprom;
7417fd7a9dSStefan Mavrodiev };
7517fd7a9dSStefan Mavrodiev 
to_lcd_olinuxino(struct drm_panel * panel)7617fd7a9dSStefan Mavrodiev static inline struct lcd_olinuxino *to_lcd_olinuxino(struct drm_panel *panel)
7717fd7a9dSStefan Mavrodiev {
7817fd7a9dSStefan Mavrodiev 	return container_of(panel, struct lcd_olinuxino, panel);
7917fd7a9dSStefan Mavrodiev }
8017fd7a9dSStefan Mavrodiev 
lcd_olinuxino_disable(struct drm_panel * panel)8117fd7a9dSStefan Mavrodiev static int lcd_olinuxino_disable(struct drm_panel *panel)
8217fd7a9dSStefan Mavrodiev {
8317fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
8417fd7a9dSStefan Mavrodiev 
8517fd7a9dSStefan Mavrodiev 	if (!lcd->enabled)
8617fd7a9dSStefan Mavrodiev 		return 0;
8717fd7a9dSStefan Mavrodiev 
8817fd7a9dSStefan Mavrodiev 	lcd->enabled = false;
8917fd7a9dSStefan Mavrodiev 
9017fd7a9dSStefan Mavrodiev 	return 0;
9117fd7a9dSStefan Mavrodiev }
9217fd7a9dSStefan Mavrodiev 
lcd_olinuxino_unprepare(struct drm_panel * panel)9317fd7a9dSStefan Mavrodiev static int lcd_olinuxino_unprepare(struct drm_panel *panel)
9417fd7a9dSStefan Mavrodiev {
9517fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
9617fd7a9dSStefan Mavrodiev 
9717fd7a9dSStefan Mavrodiev 	if (!lcd->prepared)
9817fd7a9dSStefan Mavrodiev 		return 0;
9917fd7a9dSStefan Mavrodiev 
10017fd7a9dSStefan Mavrodiev 	gpiod_set_value_cansleep(lcd->enable_gpio, 0);
10117fd7a9dSStefan Mavrodiev 	regulator_disable(lcd->supply);
10217fd7a9dSStefan Mavrodiev 
10317fd7a9dSStefan Mavrodiev 	lcd->prepared = false;
10417fd7a9dSStefan Mavrodiev 
10517fd7a9dSStefan Mavrodiev 	return 0;
10617fd7a9dSStefan Mavrodiev }
10717fd7a9dSStefan Mavrodiev 
lcd_olinuxino_prepare(struct drm_panel * panel)10817fd7a9dSStefan Mavrodiev static int lcd_olinuxino_prepare(struct drm_panel *panel)
10917fd7a9dSStefan Mavrodiev {
11017fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
11117fd7a9dSStefan Mavrodiev 	int ret;
11217fd7a9dSStefan Mavrodiev 
11317fd7a9dSStefan Mavrodiev 	if (lcd->prepared)
11417fd7a9dSStefan Mavrodiev 		return 0;
11517fd7a9dSStefan Mavrodiev 
11617fd7a9dSStefan Mavrodiev 	ret = regulator_enable(lcd->supply);
11717fd7a9dSStefan Mavrodiev 	if (ret < 0)
11817fd7a9dSStefan Mavrodiev 		return ret;
11917fd7a9dSStefan Mavrodiev 
12017fd7a9dSStefan Mavrodiev 	gpiod_set_value_cansleep(lcd->enable_gpio, 1);
12117fd7a9dSStefan Mavrodiev 	lcd->prepared = true;
12217fd7a9dSStefan Mavrodiev 
12317fd7a9dSStefan Mavrodiev 	return 0;
12417fd7a9dSStefan Mavrodiev }
12517fd7a9dSStefan Mavrodiev 
lcd_olinuxino_enable(struct drm_panel * panel)12617fd7a9dSStefan Mavrodiev static int lcd_olinuxino_enable(struct drm_panel *panel)
12717fd7a9dSStefan Mavrodiev {
12817fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
12917fd7a9dSStefan Mavrodiev 
13017fd7a9dSStefan Mavrodiev 	if (lcd->enabled)
13117fd7a9dSStefan Mavrodiev 		return 0;
13217fd7a9dSStefan Mavrodiev 
13317fd7a9dSStefan Mavrodiev 	lcd->enabled = true;
13417fd7a9dSStefan Mavrodiev 
13517fd7a9dSStefan Mavrodiev 	return 0;
13617fd7a9dSStefan Mavrodiev }
13717fd7a9dSStefan Mavrodiev 
lcd_olinuxino_get_modes(struct drm_panel * panel,struct drm_connector * connector)1380ce8ddd8SSam Ravnborg static int lcd_olinuxino_get_modes(struct drm_panel *panel,
1390ce8ddd8SSam Ravnborg 				   struct drm_connector *connector)
14017fd7a9dSStefan Mavrodiev {
14117fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
14217fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino_info *lcd_info = &lcd->eeprom.info;
14317fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino_mode *lcd_mode;
14417fd7a9dSStefan Mavrodiev 	struct drm_display_mode *mode;
14517fd7a9dSStefan Mavrodiev 	u32 i, num = 0;
14617fd7a9dSStefan Mavrodiev 
14717fd7a9dSStefan Mavrodiev 	for (i = 0; i < lcd->eeprom.num_modes; i++) {
14817fd7a9dSStefan Mavrodiev 		lcd_mode = (struct lcd_olinuxino_mode *)
14917fd7a9dSStefan Mavrodiev 			   &lcd->eeprom.reserved[i * sizeof(*lcd_mode)];
15017fd7a9dSStefan Mavrodiev 
151aa6c4364SSam Ravnborg 		mode = drm_mode_create(connector->dev);
15217fd7a9dSStefan Mavrodiev 		if (!mode) {
153aa6c4364SSam Ravnborg 			dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
15417fd7a9dSStefan Mavrodiev 				lcd_mode->hactive,
15517fd7a9dSStefan Mavrodiev 				lcd_mode->vactive,
15617fd7a9dSStefan Mavrodiev 				lcd_mode->refresh);
15717fd7a9dSStefan Mavrodiev 			continue;
15817fd7a9dSStefan Mavrodiev 		}
15917fd7a9dSStefan Mavrodiev 
16017fd7a9dSStefan Mavrodiev 		mode->clock = lcd_mode->pixelclock;
16117fd7a9dSStefan Mavrodiev 		mode->hdisplay = lcd_mode->hactive;
16217fd7a9dSStefan Mavrodiev 		mode->hsync_start = lcd_mode->hactive + lcd_mode->hfp;
16317fd7a9dSStefan Mavrodiev 		mode->hsync_end = lcd_mode->hactive + lcd_mode->hfp +
16417fd7a9dSStefan Mavrodiev 				  lcd_mode->hpw;
16517fd7a9dSStefan Mavrodiev 		mode->htotal = lcd_mode->hactive + lcd_mode->hfp +
16617fd7a9dSStefan Mavrodiev 			       lcd_mode->hpw + lcd_mode->hbp;
16717fd7a9dSStefan Mavrodiev 		mode->vdisplay = lcd_mode->vactive;
16817fd7a9dSStefan Mavrodiev 		mode->vsync_start = lcd_mode->vactive + lcd_mode->vfp;
16917fd7a9dSStefan Mavrodiev 		mode->vsync_end = lcd_mode->vactive + lcd_mode->vfp +
17017fd7a9dSStefan Mavrodiev 				  lcd_mode->vpw;
17117fd7a9dSStefan Mavrodiev 		mode->vtotal = lcd_mode->vactive + lcd_mode->vfp +
17217fd7a9dSStefan Mavrodiev 			       lcd_mode->vpw + lcd_mode->vbp;
17317fd7a9dSStefan Mavrodiev 
17417fd7a9dSStefan Mavrodiev 		/* Always make the first mode preferred */
17517fd7a9dSStefan Mavrodiev 		if (i == 0)
17617fd7a9dSStefan Mavrodiev 			mode->type |= DRM_MODE_TYPE_PREFERRED;
17717fd7a9dSStefan Mavrodiev 		mode->type |= DRM_MODE_TYPE_DRIVER;
17817fd7a9dSStefan Mavrodiev 
17917fd7a9dSStefan Mavrodiev 		drm_mode_set_name(mode);
18017fd7a9dSStefan Mavrodiev 		drm_mode_probed_add(connector, mode);
18117fd7a9dSStefan Mavrodiev 
18217fd7a9dSStefan Mavrodiev 		num++;
18317fd7a9dSStefan Mavrodiev 	}
18417fd7a9dSStefan Mavrodiev 
18517fd7a9dSStefan Mavrodiev 	connector->display_info.width_mm = lcd_info->width_mm;
18617fd7a9dSStefan Mavrodiev 	connector->display_info.height_mm = lcd_info->height_mm;
18717fd7a9dSStefan Mavrodiev 	connector->display_info.bpc = lcd_info->bpc;
18817fd7a9dSStefan Mavrodiev 
18917fd7a9dSStefan Mavrodiev 	if (lcd_info->bus_format)
19017fd7a9dSStefan Mavrodiev 		drm_display_info_set_bus_formats(&connector->display_info,
19117fd7a9dSStefan Mavrodiev 						 &lcd_info->bus_format, 1);
19217fd7a9dSStefan Mavrodiev 	connector->display_info.bus_flags = lcd_info->bus_flag;
19317fd7a9dSStefan Mavrodiev 
19417fd7a9dSStefan Mavrodiev 	return num;
19517fd7a9dSStefan Mavrodiev }
19617fd7a9dSStefan Mavrodiev 
19717fd7a9dSStefan Mavrodiev static const struct drm_panel_funcs lcd_olinuxino_funcs = {
19817fd7a9dSStefan Mavrodiev 	.disable = lcd_olinuxino_disable,
19917fd7a9dSStefan Mavrodiev 	.unprepare = lcd_olinuxino_unprepare,
20017fd7a9dSStefan Mavrodiev 	.prepare = lcd_olinuxino_prepare,
20117fd7a9dSStefan Mavrodiev 	.enable = lcd_olinuxino_enable,
20217fd7a9dSStefan Mavrodiev 	.get_modes = lcd_olinuxino_get_modes,
20317fd7a9dSStefan Mavrodiev };
20417fd7a9dSStefan Mavrodiev 
lcd_olinuxino_probe(struct i2c_client * client)20544675757SUwe Kleine-König static int lcd_olinuxino_probe(struct i2c_client *client)
20617fd7a9dSStefan Mavrodiev {
20717fd7a9dSStefan Mavrodiev 	struct device *dev = &client->dev;
20817fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino *lcd;
20917fd7a9dSStefan Mavrodiev 	u32 checksum, i;
21017fd7a9dSStefan Mavrodiev 	int ret = 0;
21117fd7a9dSStefan Mavrodiev 
21217fd7a9dSStefan Mavrodiev 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
21317fd7a9dSStefan Mavrodiev 				     I2C_FUNC_SMBUS_READ_I2C_BLOCK))
21417fd7a9dSStefan Mavrodiev 		return -ENODEV;
21517fd7a9dSStefan Mavrodiev 
21617fd7a9dSStefan Mavrodiev 	lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
21717fd7a9dSStefan Mavrodiev 	if (!lcd)
21817fd7a9dSStefan Mavrodiev 		return -ENOMEM;
21917fd7a9dSStefan Mavrodiev 
22017fd7a9dSStefan Mavrodiev 	i2c_set_clientdata(client, lcd);
22117fd7a9dSStefan Mavrodiev 	lcd->dev = dev;
22217fd7a9dSStefan Mavrodiev 	lcd->client = client;
22317fd7a9dSStefan Mavrodiev 
22417fd7a9dSStefan Mavrodiev 	mutex_init(&lcd->mutex);
22517fd7a9dSStefan Mavrodiev 
22617fd7a9dSStefan Mavrodiev 	/* Copy data into buffer */
22717fd7a9dSStefan Mavrodiev 	for (i = 0; i < LCD_OLINUXINO_DATA_LEN; i += I2C_SMBUS_BLOCK_MAX) {
22817fd7a9dSStefan Mavrodiev 		mutex_lock(&lcd->mutex);
22917fd7a9dSStefan Mavrodiev 		ret = i2c_smbus_read_i2c_block_data(client,
23017fd7a9dSStefan Mavrodiev 						    i,
23117fd7a9dSStefan Mavrodiev 						    I2C_SMBUS_BLOCK_MAX,
23217fd7a9dSStefan Mavrodiev 						    (u8 *)&lcd->eeprom + i);
23317fd7a9dSStefan Mavrodiev 		mutex_unlock(&lcd->mutex);
23417fd7a9dSStefan Mavrodiev 		if (ret < 0) {
23517fd7a9dSStefan Mavrodiev 			dev_err(dev, "error reading from device at %02x\n", i);
23617fd7a9dSStefan Mavrodiev 			return ret;
23717fd7a9dSStefan Mavrodiev 		}
23817fd7a9dSStefan Mavrodiev 	}
23917fd7a9dSStefan Mavrodiev 
24017fd7a9dSStefan Mavrodiev 	/* Check configuration checksum */
24117fd7a9dSStefan Mavrodiev 	checksum = ~crc32(~0, (u8 *)&lcd->eeprom, 252);
24217fd7a9dSStefan Mavrodiev 	if (checksum != lcd->eeprom.checksum) {
24317fd7a9dSStefan Mavrodiev 		dev_err(dev, "configuration checksum does not match!\n");
24417fd7a9dSStefan Mavrodiev 		return -EINVAL;
24517fd7a9dSStefan Mavrodiev 	}
24617fd7a9dSStefan Mavrodiev 
24717fd7a9dSStefan Mavrodiev 	/* Check magic header */
24817fd7a9dSStefan Mavrodiev 	if (lcd->eeprom.header != LCD_OLINUXINO_HEADER_MAGIC) {
24917fd7a9dSStefan Mavrodiev 		dev_err(dev, "magic header does not match\n");
25017fd7a9dSStefan Mavrodiev 		return -EINVAL;
25117fd7a9dSStefan Mavrodiev 	}
25217fd7a9dSStefan Mavrodiev 
25317fd7a9dSStefan Mavrodiev 	dev_info(dev, "Detected %s, Rev. %s, Serial: %08x\n",
25417fd7a9dSStefan Mavrodiev 		 lcd->eeprom.info.name,
25517fd7a9dSStefan Mavrodiev 		 lcd->eeprom.revision,
25617fd7a9dSStefan Mavrodiev 		 lcd->eeprom.serial);
25717fd7a9dSStefan Mavrodiev 
25817fd7a9dSStefan Mavrodiev 	/*
25917fd7a9dSStefan Mavrodiev 	 * The eeprom can hold up to 4 modes.
26017fd7a9dSStefan Mavrodiev 	 * If the stored value is bigger, overwrite it.
26117fd7a9dSStefan Mavrodiev 	 */
26217fd7a9dSStefan Mavrodiev 	if (lcd->eeprom.num_modes > 4) {
26317fd7a9dSStefan Mavrodiev 		dev_warn(dev, "invalid number of modes, falling back to 4\n");
26417fd7a9dSStefan Mavrodiev 		lcd->eeprom.num_modes = 4;
26517fd7a9dSStefan Mavrodiev 	}
26617fd7a9dSStefan Mavrodiev 
26717fd7a9dSStefan Mavrodiev 	lcd->enabled = false;
26817fd7a9dSStefan Mavrodiev 	lcd->prepared = false;
26917fd7a9dSStefan Mavrodiev 
27017fd7a9dSStefan Mavrodiev 	lcd->supply = devm_regulator_get(dev, "power");
27117fd7a9dSStefan Mavrodiev 	if (IS_ERR(lcd->supply))
27217fd7a9dSStefan Mavrodiev 		return PTR_ERR(lcd->supply);
27317fd7a9dSStefan Mavrodiev 
27417fd7a9dSStefan Mavrodiev 	lcd->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
27517fd7a9dSStefan Mavrodiev 	if (IS_ERR(lcd->enable_gpio))
27617fd7a9dSStefan Mavrodiev 		return PTR_ERR(lcd->enable_gpio);
27717fd7a9dSStefan Mavrodiev 
2789a2654c0SLaurent Pinchart 	drm_panel_init(&lcd->panel, dev, &lcd_olinuxino_funcs,
2799a2654c0SLaurent Pinchart 		       DRM_MODE_CONNECTOR_DPI);
28017fd7a9dSStefan Mavrodiev 
281d90b3b6eSSam Ravnborg 	ret = drm_panel_of_backlight(&lcd->panel);
282d90b3b6eSSam Ravnborg 	if (ret)
283d90b3b6eSSam Ravnborg 		return ret;
284d90b3b6eSSam Ravnborg 
285c3ee8c65SBernard Zhao 	drm_panel_add(&lcd->panel);
286c3ee8c65SBernard Zhao 
287c3ee8c65SBernard Zhao 	return 0;
28817fd7a9dSStefan Mavrodiev }
28917fd7a9dSStefan Mavrodiev 
lcd_olinuxino_remove(struct i2c_client * client)290ed5c2f5fSUwe Kleine-König static void lcd_olinuxino_remove(struct i2c_client *client)
29117fd7a9dSStefan Mavrodiev {
29217fd7a9dSStefan Mavrodiev 	struct lcd_olinuxino *panel = i2c_get_clientdata(client);
29317fd7a9dSStefan Mavrodiev 
29417fd7a9dSStefan Mavrodiev 	drm_panel_remove(&panel->panel);
29517fd7a9dSStefan Mavrodiev 
296d90b3b6eSSam Ravnborg 	drm_panel_disable(&panel->panel);
297d90b3b6eSSam Ravnborg 	drm_panel_unprepare(&panel->panel);
29817fd7a9dSStefan Mavrodiev }
29917fd7a9dSStefan Mavrodiev 
30017fd7a9dSStefan Mavrodiev static const struct of_device_id lcd_olinuxino_of_ids[] = {
30117fd7a9dSStefan Mavrodiev 	{ .compatible = "olimex,lcd-olinuxino" },
30217fd7a9dSStefan Mavrodiev 	{ }
30317fd7a9dSStefan Mavrodiev };
30417fd7a9dSStefan Mavrodiev MODULE_DEVICE_TABLE(of, lcd_olinuxino_of_ids);
30517fd7a9dSStefan Mavrodiev 
30617fd7a9dSStefan Mavrodiev static struct i2c_driver lcd_olinuxino_driver = {
30717fd7a9dSStefan Mavrodiev 	.driver = {
30817fd7a9dSStefan Mavrodiev 		.name = "lcd_olinuxino",
30917fd7a9dSStefan Mavrodiev 		.of_match_table = lcd_olinuxino_of_ids,
31017fd7a9dSStefan Mavrodiev 	},
311*332af828SUwe Kleine-König 	.probe = lcd_olinuxino_probe,
31217fd7a9dSStefan Mavrodiev 	.remove = lcd_olinuxino_remove,
31317fd7a9dSStefan Mavrodiev };
31417fd7a9dSStefan Mavrodiev 
31517fd7a9dSStefan Mavrodiev module_i2c_driver(lcd_olinuxino_driver);
31617fd7a9dSStefan Mavrodiev 
31717fd7a9dSStefan Mavrodiev MODULE_AUTHOR("Stefan Mavrodiev <stefan@olimex.com>");
31817fd7a9dSStefan Mavrodiev MODULE_DESCRIPTION("LCD-OLinuXino driver");
31917fd7a9dSStefan Mavrodiev MODULE_LICENSE("GPL");
320