cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

panel-olimex-lcd-olinuxino.c (7361B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * LCD-OLinuXino support for panel driver
      4 *
      5 * Copyright (C) 2018 Olimex Ltd.
      6 *   Author: Stefan Mavrodiev <stefan@olimex.com>
      7 */
      8
      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>
     14#include <linux/of.h>
     15#include <linux/regulator/consumer.h>
     16
     17#include <video/videomode.h>
     18#include <video/display_timing.h>
     19
     20#include <drm/drm_device.h>
     21#include <drm/drm_modes.h>
     22#include <drm/drm_panel.h>
     23
     24#define LCD_OLINUXINO_HEADER_MAGIC	0x4F4CB727
     25#define LCD_OLINUXINO_DATA_LEN		256
     26
     27struct lcd_olinuxino_mode {
     28	u32 pixelclock;
     29	u32 hactive;
     30	u32 hfp;
     31	u32 hbp;
     32	u32 hpw;
     33	u32 vactive;
     34	u32 vfp;
     35	u32 vbp;
     36	u32 vpw;
     37	u32 refresh;
     38	u32 flags;
     39};
     40
     41struct lcd_olinuxino_info {
     42	char name[32];
     43	u32 width_mm;
     44	u32 height_mm;
     45	u32 bpc;
     46	u32 bus_format;
     47	u32 bus_flag;
     48} __attribute__((__packed__));
     49
     50struct lcd_olinuxino_eeprom {
     51	u32 header;
     52	u32 id;
     53	char revision[4];
     54	u32 serial;
     55	struct lcd_olinuxino_info info;
     56	u32 num_modes;
     57	u8 reserved[180];
     58	u32 checksum;
     59} __attribute__((__packed__));
     60
     61struct lcd_olinuxino {
     62	struct drm_panel panel;
     63	struct device *dev;
     64	struct i2c_client *client;
     65	struct mutex mutex;
     66
     67	bool prepared;
     68	bool enabled;
     69
     70	struct regulator *supply;
     71	struct gpio_desc *enable_gpio;
     72
     73	struct lcd_olinuxino_eeprom eeprom;
     74};
     75
     76static inline struct lcd_olinuxino *to_lcd_olinuxino(struct drm_panel *panel)
     77{
     78	return container_of(panel, struct lcd_olinuxino, panel);
     79}
     80
     81static int lcd_olinuxino_disable(struct drm_panel *panel)
     82{
     83	struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
     84
     85	if (!lcd->enabled)
     86		return 0;
     87
     88	lcd->enabled = false;
     89
     90	return 0;
     91}
     92
     93static int lcd_olinuxino_unprepare(struct drm_panel *panel)
     94{
     95	struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
     96
     97	if (!lcd->prepared)
     98		return 0;
     99
    100	gpiod_set_value_cansleep(lcd->enable_gpio, 0);
    101	regulator_disable(lcd->supply);
    102
    103	lcd->prepared = false;
    104
    105	return 0;
    106}
    107
    108static int lcd_olinuxino_prepare(struct drm_panel *panel)
    109{
    110	struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
    111	int ret;
    112
    113	if (lcd->prepared)
    114		return 0;
    115
    116	ret = regulator_enable(lcd->supply);
    117	if (ret < 0)
    118		return ret;
    119
    120	gpiod_set_value_cansleep(lcd->enable_gpio, 1);
    121	lcd->prepared = true;
    122
    123	return 0;
    124}
    125
    126static int lcd_olinuxino_enable(struct drm_panel *panel)
    127{
    128	struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
    129
    130	if (lcd->enabled)
    131		return 0;
    132
    133	lcd->enabled = true;
    134
    135	return 0;
    136}
    137
    138static int lcd_olinuxino_get_modes(struct drm_panel *panel,
    139				   struct drm_connector *connector)
    140{
    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;
    145	u32 i, num = 0;
    146
    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)];
    150
    151		mode = drm_mode_create(connector->dev);
    152		if (!mode) {
    153			dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
    154				lcd_mode->hactive,
    155				lcd_mode->vactive,
    156				lcd_mode->refresh);
    157			continue;
    158		}
    159
    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 +
    164				  lcd_mode->hpw;
    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 +
    170				  lcd_mode->vpw;
    171		mode->vtotal = lcd_mode->vactive + lcd_mode->vfp +
    172			       lcd_mode->vpw + lcd_mode->vbp;
    173
    174		/* Always make the first mode preferred */
    175		if (i == 0)
    176			mode->type |= DRM_MODE_TYPE_PREFERRED;
    177		mode->type |= DRM_MODE_TYPE_DRIVER;
    178
    179		drm_mode_set_name(mode);
    180		drm_mode_probed_add(connector, mode);
    181
    182		num++;
    183	}
    184
    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;
    188
    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;
    193
    194	return num;
    195}
    196
    197static 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,
    203};
    204
    205static int lcd_olinuxino_probe(struct i2c_client *client,
    206			       const struct i2c_device_id *id)
    207{
    208	struct device *dev = &client->dev;
    209	struct lcd_olinuxino *lcd;
    210	u32 checksum, i;
    211	int ret = 0;
    212
    213	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
    214				     I2C_FUNC_SMBUS_READ_I2C_BLOCK))
    215		return -ENODEV;
    216
    217	lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
    218	if (!lcd)
    219		return -ENOMEM;
    220
    221	i2c_set_clientdata(client, lcd);
    222	lcd->dev = dev;
    223	lcd->client = client;
    224
    225	mutex_init(&lcd->mutex);
    226
    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,
    231						    i,
    232						    I2C_SMBUS_BLOCK_MAX,
    233						    (u8 *)&lcd->eeprom + i);
    234		mutex_unlock(&lcd->mutex);
    235		if (ret < 0) {
    236			dev_err(dev, "error reading from device at %02x\n", i);
    237			return ret;
    238		}
    239	}
    240
    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");
    245		return -EINVAL;
    246	}
    247
    248	/* Check magic header */
    249	if (lcd->eeprom.header != LCD_OLINUXINO_HEADER_MAGIC) {
    250		dev_err(dev, "magic header does not match\n");
    251		return -EINVAL;
    252	}
    253
    254	dev_info(dev, "Detected %s, Rev. %s, Serial: %08x\n",
    255		 lcd->eeprom.info.name,
    256		 lcd->eeprom.revision,
    257		 lcd->eeprom.serial);
    258
    259	/*
    260	 * The eeprom can hold up to 4 modes.
    261	 * If the stored value is bigger, overwrite it.
    262	 */
    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;
    266	}
    267
    268	lcd->enabled = false;
    269	lcd->prepared = false;
    270
    271	lcd->supply = devm_regulator_get(dev, "power");
    272	if (IS_ERR(lcd->supply))
    273		return PTR_ERR(lcd->supply);
    274
    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);
    278
    279	drm_panel_init(&lcd->panel, dev, &lcd_olinuxino_funcs,
    280		       DRM_MODE_CONNECTOR_DPI);
    281
    282	ret = drm_panel_of_backlight(&lcd->panel);
    283	if (ret)
    284		return ret;
    285
    286	drm_panel_add(&lcd->panel);
    287
    288	return 0;
    289}
    290
    291static int lcd_olinuxino_remove(struct i2c_client *client)
    292{
    293	struct lcd_olinuxino *panel = i2c_get_clientdata(client);
    294
    295	drm_panel_remove(&panel->panel);
    296
    297	drm_panel_disable(&panel->panel);
    298	drm_panel_unprepare(&panel->panel);
    299
    300	return 0;
    301}
    302
    303static const struct of_device_id lcd_olinuxino_of_ids[] = {
    304	{ .compatible = "olimex,lcd-olinuxino" },
    305	{ }
    306};
    307MODULE_DEVICE_TABLE(of, lcd_olinuxino_of_ids);
    308
    309static struct i2c_driver lcd_olinuxino_driver = {
    310	.driver = {
    311		.name = "lcd_olinuxino",
    312		.of_match_table = lcd_olinuxino_of_ids,
    313	},
    314	.probe = lcd_olinuxino_probe,
    315	.remove = lcd_olinuxino_remove,
    316};
    317
    318module_i2c_driver(lcd_olinuxino_driver);
    319
    320MODULE_AUTHOR("Stefan Mavrodiev <stefan@olimex.com>");
    321MODULE_DESCRIPTION("LCD-OLinuXino driver");
    322MODULE_LICENSE("GPL");