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-sony-acx565akm.c (16304B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Sony ACX565AKM LCD Panel driver
      4 *
      5 * Copyright (C) 2019 Texas Instruments Incorporated
      6 *
      7 * Based on the omapdrm-specific panel-sony-acx565akm driver
      8 *
      9 * Copyright (C) 2010 Nokia Corporation
     10 * Author: Imre Deak <imre.deak@nokia.com>
     11 */
     12
     13/*
     14 * TODO (to be addressed with hardware access to test the changes):
     15 *
     16 * - Update backlight support to use backlight_update_status() etc.
     17 * - Use prepare/unprepare for the basic power on/off of the backligt
     18 */
     19
     20#include <linux/backlight.h>
     21#include <linux/delay.h>
     22#include <linux/gpio/consumer.h>
     23#include <linux/jiffies.h>
     24#include <linux/module.h>
     25#include <linux/mutex.h>
     26#include <linux/sched.h>
     27#include <linux/spi/spi.h>
     28#include <video/mipi_display.h>
     29
     30#include <drm/drm_connector.h>
     31#include <drm/drm_modes.h>
     32#include <drm/drm_panel.h>
     33
     34#define CTRL_DISP_BRIGHTNESS_CTRL_ON		BIT(5)
     35#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON		BIT(4)
     36#define CTRL_DISP_BACKLIGHT_ON			BIT(2)
     37#define CTRL_DISP_AUTO_BRIGHTNESS_ON		BIT(1)
     38
     39#define MIPID_CMD_WRITE_CABC		0x55
     40#define MIPID_CMD_READ_CABC		0x56
     41
     42#define MIPID_VER_LPH8923		3
     43#define MIPID_VER_LS041Y3		4
     44#define MIPID_VER_L4F00311		8
     45#define MIPID_VER_ACX565AKM		9
     46
     47struct acx565akm_panel {
     48	struct drm_panel panel;
     49
     50	struct spi_device *spi;
     51	struct gpio_desc *reset_gpio;
     52	struct backlight_device *backlight;
     53
     54	struct mutex mutex;
     55
     56	const char *name;
     57	u8 display_id[3];
     58	int model;
     59	int revision;
     60	bool has_bc;
     61	bool has_cabc;
     62
     63	bool enabled;
     64	unsigned int cabc_mode;
     65	/*
     66	 * Next value of jiffies when we can issue the next sleep in/out
     67	 * command.
     68	 */
     69	unsigned long hw_guard_end;
     70	unsigned long hw_guard_wait;		/* max guard time in jiffies */
     71};
     72
     73#define to_acx565akm_device(p) container_of(p, struct acx565akm_panel, panel)
     74
     75static void acx565akm_transfer(struct acx565akm_panel *lcd, int cmd,
     76			      const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
     77{
     78	struct spi_message	m;
     79	struct spi_transfer	*x, xfer[5];
     80	int			ret;
     81
     82	spi_message_init(&m);
     83
     84	memset(xfer, 0, sizeof(xfer));
     85	x = &xfer[0];
     86
     87	cmd &=  0xff;
     88	x->tx_buf = &cmd;
     89	x->bits_per_word = 9;
     90	x->len = 2;
     91
     92	if (rlen > 1 && wlen == 0) {
     93		/*
     94		 * Between the command and the response data there is a
     95		 * dummy clock cycle. Add an extra bit after the command
     96		 * word to account for this.
     97		 */
     98		x->bits_per_word = 10;
     99		cmd <<= 1;
    100	}
    101	spi_message_add_tail(x, &m);
    102
    103	if (wlen) {
    104		x++;
    105		x->tx_buf = wbuf;
    106		x->len = wlen;
    107		x->bits_per_word = 9;
    108		spi_message_add_tail(x, &m);
    109	}
    110
    111	if (rlen) {
    112		x++;
    113		x->rx_buf	= rbuf;
    114		x->len		= rlen;
    115		spi_message_add_tail(x, &m);
    116	}
    117
    118	ret = spi_sync(lcd->spi, &m);
    119	if (ret < 0)
    120		dev_dbg(&lcd->spi->dev, "spi_sync %d\n", ret);
    121}
    122
    123static inline void acx565akm_cmd(struct acx565akm_panel *lcd, int cmd)
    124{
    125	acx565akm_transfer(lcd, cmd, NULL, 0, NULL, 0);
    126}
    127
    128static inline void acx565akm_write(struct acx565akm_panel *lcd,
    129			       int reg, const u8 *buf, int len)
    130{
    131	acx565akm_transfer(lcd, reg, buf, len, NULL, 0);
    132}
    133
    134static inline void acx565akm_read(struct acx565akm_panel *lcd,
    135			      int reg, u8 *buf, int len)
    136{
    137	acx565akm_transfer(lcd, reg, NULL, 0, buf, len);
    138}
    139
    140/* -----------------------------------------------------------------------------
    141 * Auto Brightness Control Via sysfs
    142 */
    143
    144static unsigned int acx565akm_get_cabc_mode(struct acx565akm_panel *lcd)
    145{
    146	return lcd->cabc_mode;
    147}
    148
    149static void acx565akm_set_cabc_mode(struct acx565akm_panel *lcd,
    150				    unsigned int mode)
    151{
    152	u16 cabc_ctrl;
    153
    154	lcd->cabc_mode = mode;
    155	if (!lcd->enabled)
    156		return;
    157	cabc_ctrl = 0;
    158	acx565akm_read(lcd, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1);
    159	cabc_ctrl &= ~3;
    160	cabc_ctrl |= (1 << 8) | (mode & 3);
    161	acx565akm_write(lcd, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2);
    162}
    163
    164static unsigned int acx565akm_get_hw_cabc_mode(struct acx565akm_panel *lcd)
    165{
    166	u8 cabc_ctrl;
    167
    168	acx565akm_read(lcd, MIPID_CMD_READ_CABC, &cabc_ctrl, 1);
    169	return cabc_ctrl & 3;
    170}
    171
    172static const char * const acx565akm_cabc_modes[] = {
    173	"off",		/* always used when CABC is not supported */
    174	"ui",
    175	"still-image",
    176	"moving-image",
    177};
    178
    179static ssize_t cabc_mode_show(struct device *dev,
    180			      struct device_attribute *attr,
    181			      char *buf)
    182{
    183	struct acx565akm_panel *lcd = dev_get_drvdata(dev);
    184	const char *mode_str;
    185	int mode;
    186
    187	if (!lcd->has_cabc)
    188		mode = 0;
    189	else
    190		mode = acx565akm_get_cabc_mode(lcd);
    191
    192	mode_str = "unknown";
    193	if (mode >= 0 && mode < ARRAY_SIZE(acx565akm_cabc_modes))
    194		mode_str = acx565akm_cabc_modes[mode];
    195
    196	return sprintf(buf, "%s\n", mode_str);
    197}
    198
    199static ssize_t cabc_mode_store(struct device *dev,
    200			       struct device_attribute *attr,
    201			       const char *buf, size_t count)
    202{
    203	struct acx565akm_panel *lcd = dev_get_drvdata(dev);
    204	unsigned int i;
    205
    206	for (i = 0; i < ARRAY_SIZE(acx565akm_cabc_modes); i++) {
    207		const char *mode_str = acx565akm_cabc_modes[i];
    208		int cmp_len = strlen(mode_str);
    209
    210		if (count > 0 && buf[count - 1] == '\n')
    211			count--;
    212		if (count != cmp_len)
    213			continue;
    214
    215		if (strncmp(buf, mode_str, cmp_len) == 0)
    216			break;
    217	}
    218
    219	if (i == ARRAY_SIZE(acx565akm_cabc_modes))
    220		return -EINVAL;
    221
    222	if (!lcd->has_cabc && i != 0)
    223		return -EINVAL;
    224
    225	mutex_lock(&lcd->mutex);
    226	acx565akm_set_cabc_mode(lcd, i);
    227	mutex_unlock(&lcd->mutex);
    228
    229	return count;
    230}
    231
    232static ssize_t cabc_available_modes_show(struct device *dev,
    233					 struct device_attribute *attr,
    234					 char *buf)
    235{
    236	struct acx565akm_panel *lcd = dev_get_drvdata(dev);
    237	unsigned int i;
    238	size_t len = 0;
    239
    240	if (!lcd->has_cabc)
    241		return sprintf(buf, "%s\n", acx565akm_cabc_modes[0]);
    242
    243	for (i = 0; i < ARRAY_SIZE(acx565akm_cabc_modes); i++)
    244		len += sprintf(&buf[len], "%s%s", i ? " " : "",
    245			       acx565akm_cabc_modes[i]);
    246
    247	buf[len++] = '\n';
    248
    249	return len;
    250}
    251
    252static DEVICE_ATTR_RW(cabc_mode);
    253static DEVICE_ATTR_RO(cabc_available_modes);
    254
    255static struct attribute *acx565akm_cabc_attrs[] = {
    256	&dev_attr_cabc_mode.attr,
    257	&dev_attr_cabc_available_modes.attr,
    258	NULL,
    259};
    260
    261static const struct attribute_group acx565akm_cabc_attr_group = {
    262	.attrs = acx565akm_cabc_attrs,
    263};
    264
    265/* -----------------------------------------------------------------------------
    266 * Backlight Device
    267 */
    268
    269static int acx565akm_get_actual_brightness(struct acx565akm_panel *lcd)
    270{
    271	u8 bv;
    272
    273	acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_BRIGHTNESS, &bv, 1);
    274
    275	return bv;
    276}
    277
    278static void acx565akm_set_brightness(struct acx565akm_panel *lcd, int level)
    279{
    280	u16 ctrl;
    281	int bv;
    282
    283	bv = level | (1 << 8);
    284	acx565akm_write(lcd, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, (u8 *)&bv, 2);
    285
    286	acx565akm_read(lcd, MIPI_DCS_GET_CONTROL_DISPLAY, (u8 *)&ctrl, 1);
    287	if (level)
    288		ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON |
    289			CTRL_DISP_BACKLIGHT_ON;
    290	else
    291		ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON |
    292			  CTRL_DISP_BACKLIGHT_ON);
    293
    294	ctrl |= 1 << 8;
    295	acx565akm_write(lcd, MIPI_DCS_WRITE_CONTROL_DISPLAY, (u8 *)&ctrl, 2);
    296}
    297
    298static int acx565akm_bl_update_status_locked(struct backlight_device *dev)
    299{
    300	struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
    301	int level;
    302
    303	if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
    304	    dev->props.power == FB_BLANK_UNBLANK)
    305		level = dev->props.brightness;
    306	else
    307		level = 0;
    308
    309	acx565akm_set_brightness(lcd, level);
    310
    311	return 0;
    312}
    313
    314static int acx565akm_bl_update_status(struct backlight_device *dev)
    315{
    316	struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
    317	int ret;
    318
    319	mutex_lock(&lcd->mutex);
    320	ret = acx565akm_bl_update_status_locked(dev);
    321	mutex_unlock(&lcd->mutex);
    322
    323	return ret;
    324}
    325
    326static int acx565akm_bl_get_intensity(struct backlight_device *dev)
    327{
    328	struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
    329	unsigned int intensity;
    330
    331	mutex_lock(&lcd->mutex);
    332
    333	if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
    334	    dev->props.power == FB_BLANK_UNBLANK)
    335		intensity = acx565akm_get_actual_brightness(lcd);
    336	else
    337		intensity = 0;
    338
    339	mutex_unlock(&lcd->mutex);
    340
    341	return intensity;
    342}
    343
    344static const struct backlight_ops acx565akm_bl_ops = {
    345	.get_brightness = acx565akm_bl_get_intensity,
    346	.update_status  = acx565akm_bl_update_status,
    347};
    348
    349static int acx565akm_backlight_init(struct acx565akm_panel *lcd)
    350{
    351	struct backlight_properties props = {
    352		.fb_blank = FB_BLANK_UNBLANK,
    353		.power = FB_BLANK_UNBLANK,
    354		.type = BACKLIGHT_RAW,
    355	};
    356	int ret;
    357
    358	lcd->backlight = backlight_device_register(lcd->name, &lcd->spi->dev,
    359						   lcd, &acx565akm_bl_ops,
    360						   &props);
    361	if (IS_ERR(lcd->backlight)) {
    362		ret = PTR_ERR(lcd->backlight);
    363		lcd->backlight = NULL;
    364		return ret;
    365	}
    366
    367	if (lcd->has_cabc) {
    368		ret = sysfs_create_group(&lcd->backlight->dev.kobj,
    369					 &acx565akm_cabc_attr_group);
    370		if (ret < 0) {
    371			dev_err(&lcd->spi->dev,
    372				"%s failed to create sysfs files\n", __func__);
    373			backlight_device_unregister(lcd->backlight);
    374			return ret;
    375		}
    376
    377		lcd->cabc_mode = acx565akm_get_hw_cabc_mode(lcd);
    378	}
    379
    380	lcd->backlight->props.max_brightness = 255;
    381	lcd->backlight->props.brightness = acx565akm_get_actual_brightness(lcd);
    382
    383	acx565akm_bl_update_status_locked(lcd->backlight);
    384
    385	return 0;
    386}
    387
    388static void acx565akm_backlight_cleanup(struct acx565akm_panel *lcd)
    389{
    390	if (lcd->has_cabc)
    391		sysfs_remove_group(&lcd->backlight->dev.kobj,
    392				   &acx565akm_cabc_attr_group);
    393
    394	backlight_device_unregister(lcd->backlight);
    395}
    396
    397/* -----------------------------------------------------------------------------
    398 * DRM Bridge Operations
    399 */
    400
    401static void acx565akm_set_sleep_mode(struct acx565akm_panel *lcd, int on)
    402{
    403	int cmd = on ? MIPI_DCS_ENTER_SLEEP_MODE : MIPI_DCS_EXIT_SLEEP_MODE;
    404	unsigned long wait;
    405
    406	/*
    407	 * We have to keep 120msec between sleep in/out commands.
    408	 * (8.2.15, 8.2.16).
    409	 */
    410	wait = lcd->hw_guard_end - jiffies;
    411	if ((long)wait > 0 && wait <= lcd->hw_guard_wait) {
    412		set_current_state(TASK_UNINTERRUPTIBLE);
    413		schedule_timeout(wait);
    414	}
    415
    416	acx565akm_cmd(lcd, cmd);
    417
    418	lcd->hw_guard_wait = msecs_to_jiffies(120);
    419	lcd->hw_guard_end = jiffies + lcd->hw_guard_wait;
    420}
    421
    422static void acx565akm_set_display_state(struct acx565akm_panel *lcd,
    423					int enabled)
    424{
    425	int cmd = enabled ? MIPI_DCS_SET_DISPLAY_ON : MIPI_DCS_SET_DISPLAY_OFF;
    426
    427	acx565akm_cmd(lcd, cmd);
    428}
    429
    430static int acx565akm_power_on(struct acx565akm_panel *lcd)
    431{
    432	/*FIXME tweak me */
    433	msleep(50);
    434
    435	gpiod_set_value(lcd->reset_gpio, 1);
    436
    437	if (lcd->enabled) {
    438		dev_dbg(&lcd->spi->dev, "panel already enabled\n");
    439		return 0;
    440	}
    441
    442	/*
    443	 * We have to meet all the following delay requirements:
    444	 * 1. tRW: reset pulse width 10usec (7.12.1)
    445	 * 2. tRT: reset cancel time 5msec (7.12.1)
    446	 * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst
    447	 *    case (7.6.2)
    448	 * 4. 120msec before the sleep out command (7.12.1)
    449	 */
    450	msleep(120);
    451
    452	acx565akm_set_sleep_mode(lcd, 0);
    453	lcd->enabled = true;
    454
    455	/* 5msec between sleep out and the next command. (8.2.16) */
    456	usleep_range(5000, 10000);
    457	acx565akm_set_display_state(lcd, 1);
    458	acx565akm_set_cabc_mode(lcd, lcd->cabc_mode);
    459
    460	return acx565akm_bl_update_status_locked(lcd->backlight);
    461}
    462
    463static void acx565akm_power_off(struct acx565akm_panel *lcd)
    464{
    465	if (!lcd->enabled)
    466		return;
    467
    468	acx565akm_set_display_state(lcd, 0);
    469	acx565akm_set_sleep_mode(lcd, 1);
    470	lcd->enabled = false;
    471	/*
    472	 * We have to provide PCLK,HS,VS signals for 2 frames (worst case
    473	 * ~50msec) after sending the sleep in command and asserting the
    474	 * reset signal. We probably could assert the reset w/o the delay
    475	 * but we still delay to avoid possible artifacts. (7.6.1)
    476	 */
    477	msleep(50);
    478
    479	gpiod_set_value(lcd->reset_gpio, 0);
    480
    481	/* FIXME need to tweak this delay */
    482	msleep(100);
    483}
    484
    485static int acx565akm_disable(struct drm_panel *panel)
    486{
    487	struct acx565akm_panel *lcd = to_acx565akm_device(panel);
    488
    489	mutex_lock(&lcd->mutex);
    490	acx565akm_power_off(lcd);
    491	mutex_unlock(&lcd->mutex);
    492
    493	return 0;
    494}
    495
    496static int acx565akm_enable(struct drm_panel *panel)
    497{
    498	struct acx565akm_panel *lcd = to_acx565akm_device(panel);
    499
    500	mutex_lock(&lcd->mutex);
    501	acx565akm_power_on(lcd);
    502	mutex_unlock(&lcd->mutex);
    503
    504	return 0;
    505}
    506
    507static const struct drm_display_mode acx565akm_mode = {
    508	.clock = 24000,
    509	.hdisplay = 800,
    510	.hsync_start = 800 + 28,
    511	.hsync_end = 800 + 28 + 4,
    512	.htotal = 800 + 28 + 4 + 24,
    513	.vdisplay = 480,
    514	.vsync_start = 480 + 3,
    515	.vsync_end = 480 + 3 + 3,
    516	.vtotal = 480 + 3 + 3 + 4,
    517	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
    518	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
    519	.width_mm = 77,
    520	.height_mm = 46,
    521};
    522
    523static int acx565akm_get_modes(struct drm_panel *panel,
    524			       struct drm_connector *connector)
    525{
    526	struct drm_display_mode *mode;
    527
    528	mode = drm_mode_duplicate(connector->dev, &acx565akm_mode);
    529	if (!mode)
    530		return -ENOMEM;
    531
    532	drm_mode_set_name(mode);
    533	drm_mode_probed_add(connector, mode);
    534
    535	connector->display_info.width_mm = acx565akm_mode.width_mm;
    536	connector->display_info.height_mm = acx565akm_mode.height_mm;
    537	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
    538					  | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
    539					  | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
    540
    541	return 1;
    542}
    543
    544static const struct drm_panel_funcs acx565akm_funcs = {
    545	.disable = acx565akm_disable,
    546	.enable = acx565akm_enable,
    547	.get_modes = acx565akm_get_modes,
    548};
    549
    550/* -----------------------------------------------------------------------------
    551 * Probe, Detect and Remove
    552 */
    553
    554static int acx565akm_detect(struct acx565akm_panel *lcd)
    555{
    556	__be32 value;
    557	u32 status;
    558	int ret = 0;
    559
    560	/*
    561	 * After being taken out of reset the panel needs 5ms before the first
    562	 * command can be sent.
    563	 */
    564	gpiod_set_value(lcd->reset_gpio, 1);
    565	usleep_range(5000, 10000);
    566
    567	acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_STATUS, (u8 *)&value, 4);
    568	status = __be32_to_cpu(value);
    569	lcd->enabled = (status & (1 << 17)) && (status & (1 << 10));
    570
    571	dev_dbg(&lcd->spi->dev,
    572		"LCD panel %s by bootloader (status 0x%04x)\n",
    573		lcd->enabled ? "enabled" : "disabled ", status);
    574
    575	acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_ID, lcd->display_id, 3);
    576	dev_dbg(&lcd->spi->dev, "MIPI display ID: %02x%02x%02x\n",
    577		lcd->display_id[0], lcd->display_id[1], lcd->display_id[2]);
    578
    579	switch (lcd->display_id[0]) {
    580	case 0x10:
    581		lcd->model = MIPID_VER_ACX565AKM;
    582		lcd->name = "acx565akm";
    583		lcd->has_bc = 1;
    584		lcd->has_cabc = 1;
    585		break;
    586	case 0x29:
    587		lcd->model = MIPID_VER_L4F00311;
    588		lcd->name = "l4f00311";
    589		break;
    590	case 0x45:
    591		lcd->model = MIPID_VER_LPH8923;
    592		lcd->name = "lph8923";
    593		break;
    594	case 0x83:
    595		lcd->model = MIPID_VER_LS041Y3;
    596		lcd->name = "ls041y3";
    597		break;
    598	default:
    599		lcd->name = "unknown";
    600		dev_err(&lcd->spi->dev, "unknown display ID\n");
    601		ret = -ENODEV;
    602		goto done;
    603	}
    604
    605	lcd->revision = lcd->display_id[1];
    606
    607	dev_info(&lcd->spi->dev, "%s rev %02x panel detected\n",
    608		 lcd->name, lcd->revision);
    609
    610done:
    611	if (!lcd->enabled)
    612		gpiod_set_value(lcd->reset_gpio, 0);
    613
    614	return ret;
    615}
    616
    617static int acx565akm_probe(struct spi_device *spi)
    618{
    619	struct acx565akm_panel *lcd;
    620	int ret;
    621
    622	lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
    623	if (!lcd)
    624		return -ENOMEM;
    625
    626	spi_set_drvdata(spi, lcd);
    627	spi->mode = SPI_MODE_3;
    628
    629	lcd->spi = spi;
    630	mutex_init(&lcd->mutex);
    631
    632	lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
    633	if (IS_ERR(lcd->reset_gpio)) {
    634		dev_err(&spi->dev, "failed to get reset GPIO\n");
    635		return PTR_ERR(lcd->reset_gpio);
    636	}
    637
    638	ret = acx565akm_detect(lcd);
    639	if (ret < 0) {
    640		dev_err(&spi->dev, "panel detection failed\n");
    641		return ret;
    642	}
    643
    644	if (lcd->has_bc) {
    645		ret = acx565akm_backlight_init(lcd);
    646		if (ret < 0)
    647			return ret;
    648	}
    649
    650	drm_panel_init(&lcd->panel, &lcd->spi->dev, &acx565akm_funcs,
    651		       DRM_MODE_CONNECTOR_DPI);
    652
    653	drm_panel_add(&lcd->panel);
    654
    655	return 0;
    656}
    657
    658static void acx565akm_remove(struct spi_device *spi)
    659{
    660	struct acx565akm_panel *lcd = spi_get_drvdata(spi);
    661
    662	drm_panel_remove(&lcd->panel);
    663
    664	if (lcd->has_bc)
    665		acx565akm_backlight_cleanup(lcd);
    666
    667	drm_panel_disable(&lcd->panel);
    668	drm_panel_unprepare(&lcd->panel);
    669}
    670
    671static const struct of_device_id acx565akm_of_match[] = {
    672	{ .compatible = "sony,acx565akm", },
    673	{ /* sentinel */ },
    674};
    675
    676MODULE_DEVICE_TABLE(of, acx565akm_of_match);
    677
    678static const struct spi_device_id acx565akm_ids[] = {
    679	{ "acx565akm", 0 },
    680	{ /* sentinel */ }
    681};
    682
    683MODULE_DEVICE_TABLE(spi, acx565akm_ids);
    684
    685static struct spi_driver acx565akm_driver = {
    686	.probe		= acx565akm_probe,
    687	.remove		= acx565akm_remove,
    688	.id_table	= acx565akm_ids,
    689	.driver		= {
    690		.name	= "panel-sony-acx565akm",
    691		.of_match_table = acx565akm_of_match,
    692	},
    693};
    694
    695module_spi_driver(acx565akm_driver);
    696
    697MODULE_AUTHOR("Nokia Corporation");
    698MODULE_DESCRIPTION("Sony ACX565AKM LCD Panel Driver");
    699MODULE_LICENSE("GPL");