162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Panel driver for the ARM Versatile family reference designs from
462306a36Sopenharmony_ci * ARM Limited.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author:
762306a36Sopenharmony_ci * Linus Walleij <linus.wallei@linaro.org>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * On the Versatile AB, these panels come mounted on daughterboards
1062306a36Sopenharmony_ci * named "IB1" or "IB2" (Interface Board 1 & 2 respectively.) They
1162306a36Sopenharmony_ci * are documented in ARM DUI 0225D Appendix C and D. These daughter
1262306a36Sopenharmony_ci * boards support TFT display panels.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * - The IB1 is a passive board where the display connector defines a
1562306a36Sopenharmony_ci *   few wires for encoding the display type for autodetection,
1662306a36Sopenharmony_ci *   suitable display settings can then be looked up from this setting.
1762306a36Sopenharmony_ci *   The magic bits can be read out from the system controller.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * - The IB2 is a more complex board intended for GSM phone development
2062306a36Sopenharmony_ci *   with some logic and a control register, which needs to be accessed
2162306a36Sopenharmony_ci *   and the board display needs to be turned on explicitly.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * On the Versatile PB, a special CLCD adaptor board is available
2462306a36Sopenharmony_ci * supporting the same displays as the Versatile AB, plus one more
2562306a36Sopenharmony_ci * Epson QCIF display.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <linux/bitops.h>
3062306a36Sopenharmony_ci#include <linux/init.h>
3162306a36Sopenharmony_ci#include <linux/kernel.h>
3262306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
3362306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
3462306a36Sopenharmony_ci#include <linux/module.h>
3562306a36Sopenharmony_ci#include <linux/platform_device.h>
3662306a36Sopenharmony_ci#include <linux/regmap.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <video/of_videomode.h>
3962306a36Sopenharmony_ci#include <video/videomode.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <drm/drm_modes.h>
4262306a36Sopenharmony_ci#include <drm/drm_panel.h>
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/*
4562306a36Sopenharmony_ci * This configuration register in the Versatile and RealView
4662306a36Sopenharmony_ci * family is uniformly present but appears more and more
4762306a36Sopenharmony_ci * unutilized starting with the RealView series.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ci#define SYS_CLCD			0x50
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* The Versatile can detect the connected panel type */
5262306a36Sopenharmony_ci#define SYS_CLCD_CLCDID_MASK		(BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12))
5362306a36Sopenharmony_ci#define SYS_CLCD_ID_SANYO_3_8		(0x00 << 8)
5462306a36Sopenharmony_ci#define SYS_CLCD_ID_SHARP_8_4		(0x01 << 8)
5562306a36Sopenharmony_ci#define SYS_CLCD_ID_EPSON_2_2		(0x02 << 8)
5662306a36Sopenharmony_ci#define SYS_CLCD_ID_SANYO_2_5		(0x07 << 8)
5762306a36Sopenharmony_ci#define SYS_CLCD_ID_VGA			(0x1f << 8)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* IB2 control register for the Versatile daughterboard */
6062306a36Sopenharmony_ci#define IB2_CTRL			0x00
6162306a36Sopenharmony_ci#define IB2_CTRL_LCD_SD			BIT(1) /* 1 = shut down LCD */
6262306a36Sopenharmony_ci#define IB2_CTRL_LCD_BL_ON		BIT(0)
6362306a36Sopenharmony_ci#define IB2_CTRL_LCD_MASK		(BIT(0)|BIT(1))
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/**
6662306a36Sopenharmony_ci * struct versatile_panel_type - lookup struct for the supported panels
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cistruct versatile_panel_type {
6962306a36Sopenharmony_ci	/**
7062306a36Sopenharmony_ci	 * @name: the name of this panel
7162306a36Sopenharmony_ci	 */
7262306a36Sopenharmony_ci	const char *name;
7362306a36Sopenharmony_ci	/**
7462306a36Sopenharmony_ci	 * @magic: the magic value from the detection register
7562306a36Sopenharmony_ci	 */
7662306a36Sopenharmony_ci	u32 magic;
7762306a36Sopenharmony_ci	/**
7862306a36Sopenharmony_ci	 * @mode: the DRM display mode for this panel
7962306a36Sopenharmony_ci	 */
8062306a36Sopenharmony_ci	struct drm_display_mode mode;
8162306a36Sopenharmony_ci	/**
8262306a36Sopenharmony_ci	 * @bus_flags: the DRM bus flags for this panel e.g. inverted clock
8362306a36Sopenharmony_ci	 */
8462306a36Sopenharmony_ci	u32 bus_flags;
8562306a36Sopenharmony_ci	/**
8662306a36Sopenharmony_ci	 * @width_mm: the panel width in mm
8762306a36Sopenharmony_ci	 */
8862306a36Sopenharmony_ci	u32 width_mm;
8962306a36Sopenharmony_ci	/**
9062306a36Sopenharmony_ci	 * @height_mm: the panel height in mm
9162306a36Sopenharmony_ci	 */
9262306a36Sopenharmony_ci	u32 height_mm;
9362306a36Sopenharmony_ci	/**
9462306a36Sopenharmony_ci	 * @ib2: the panel may be connected on an IB2 daughterboard
9562306a36Sopenharmony_ci	 */
9662306a36Sopenharmony_ci	bool ib2;
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/**
10062306a36Sopenharmony_ci * struct versatile_panel - state container for the Versatile panels
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistruct versatile_panel {
10362306a36Sopenharmony_ci	/**
10462306a36Sopenharmony_ci	 * @dev: the container device
10562306a36Sopenharmony_ci	 */
10662306a36Sopenharmony_ci	struct device *dev;
10762306a36Sopenharmony_ci	/**
10862306a36Sopenharmony_ci	 * @panel: the DRM panel instance for this device
10962306a36Sopenharmony_ci	 */
11062306a36Sopenharmony_ci	struct drm_panel panel;
11162306a36Sopenharmony_ci	/**
11262306a36Sopenharmony_ci	 * @panel_type: the Versatile panel type as detected
11362306a36Sopenharmony_ci	 */
11462306a36Sopenharmony_ci	const struct versatile_panel_type *panel_type;
11562306a36Sopenharmony_ci	/**
11662306a36Sopenharmony_ci	 * @map: map to the parent syscon where the main register reside
11762306a36Sopenharmony_ci	 */
11862306a36Sopenharmony_ci	struct regmap *map;
11962306a36Sopenharmony_ci	/**
12062306a36Sopenharmony_ci	 * @ib2_map: map to the IB2 syscon, if applicable
12162306a36Sopenharmony_ci	 */
12262306a36Sopenharmony_ci	struct regmap *ib2_map;
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic const struct versatile_panel_type versatile_panels[] = {
12662306a36Sopenharmony_ci	/*
12762306a36Sopenharmony_ci	 * Sanyo TM38QV67A02A - 3.8 inch QVGA (320x240) Color TFT
12862306a36Sopenharmony_ci	 * found on the Versatile AB IB1 connector or the Versatile
12962306a36Sopenharmony_ci	 * PB adaptor board connector.
13062306a36Sopenharmony_ci	 */
13162306a36Sopenharmony_ci	{
13262306a36Sopenharmony_ci		.name = "Sanyo TM38QV67A02A",
13362306a36Sopenharmony_ci		.magic = SYS_CLCD_ID_SANYO_3_8,
13462306a36Sopenharmony_ci		.width_mm = 79,
13562306a36Sopenharmony_ci		.height_mm = 54,
13662306a36Sopenharmony_ci		.mode = {
13762306a36Sopenharmony_ci			.clock = 10000,
13862306a36Sopenharmony_ci			.hdisplay = 320,
13962306a36Sopenharmony_ci			.hsync_start = 320 + 6,
14062306a36Sopenharmony_ci			.hsync_end = 320 + 6 + 6,
14162306a36Sopenharmony_ci			.htotal = 320 + 6 + 6 + 6,
14262306a36Sopenharmony_ci			.vdisplay = 240,
14362306a36Sopenharmony_ci			.vsync_start = 240 + 5,
14462306a36Sopenharmony_ci			.vsync_end = 240 + 5 + 6,
14562306a36Sopenharmony_ci			.vtotal = 240 + 5 + 6 + 5,
14662306a36Sopenharmony_ci			.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
14762306a36Sopenharmony_ci		},
14862306a36Sopenharmony_ci	},
14962306a36Sopenharmony_ci	/*
15062306a36Sopenharmony_ci	 * Sharp LQ084V1DG21 640x480 VGA Color TFT module
15162306a36Sopenharmony_ci	 * found on the Versatile AB IB1 connector or the Versatile
15262306a36Sopenharmony_ci	 * PB adaptor board connector.
15362306a36Sopenharmony_ci	 */
15462306a36Sopenharmony_ci	{
15562306a36Sopenharmony_ci		.name = "Sharp LQ084V1DG21",
15662306a36Sopenharmony_ci		.magic = SYS_CLCD_ID_SHARP_8_4,
15762306a36Sopenharmony_ci		.width_mm = 171,
15862306a36Sopenharmony_ci		.height_mm = 130,
15962306a36Sopenharmony_ci		.mode = {
16062306a36Sopenharmony_ci			.clock = 25000,
16162306a36Sopenharmony_ci			.hdisplay = 640,
16262306a36Sopenharmony_ci			.hsync_start = 640 + 24,
16362306a36Sopenharmony_ci			.hsync_end = 640 + 24 + 96,
16462306a36Sopenharmony_ci			.htotal = 640 + 24 + 96 + 24,
16562306a36Sopenharmony_ci			.vdisplay = 480,
16662306a36Sopenharmony_ci			.vsync_start = 480 + 11,
16762306a36Sopenharmony_ci			.vsync_end = 480 + 11 + 2,
16862306a36Sopenharmony_ci			.vtotal = 480 + 11 + 2 + 32,
16962306a36Sopenharmony_ci			.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
17062306a36Sopenharmony_ci		},
17162306a36Sopenharmony_ci	},
17262306a36Sopenharmony_ci	/*
17362306a36Sopenharmony_ci	 * Epson L2F50113T00 - 2.2 inch QCIF 176x220 Color TFT
17462306a36Sopenharmony_ci	 * found on the Versatile PB adaptor board connector.
17562306a36Sopenharmony_ci	 */
17662306a36Sopenharmony_ci	{
17762306a36Sopenharmony_ci		.name = "Epson L2F50113T00",
17862306a36Sopenharmony_ci		.magic = SYS_CLCD_ID_EPSON_2_2,
17962306a36Sopenharmony_ci		.width_mm = 34,
18062306a36Sopenharmony_ci		.height_mm = 45,
18162306a36Sopenharmony_ci		.mode = {
18262306a36Sopenharmony_ci			.clock = 62500,
18362306a36Sopenharmony_ci			.hdisplay = 176,
18462306a36Sopenharmony_ci			.hsync_start = 176 + 2,
18562306a36Sopenharmony_ci			.hsync_end = 176 + 2 + 3,
18662306a36Sopenharmony_ci			.htotal = 176 + 2 + 3 + 3,
18762306a36Sopenharmony_ci			.vdisplay = 220,
18862306a36Sopenharmony_ci			.vsync_start = 220 + 0,
18962306a36Sopenharmony_ci			.vsync_end = 220 + 0 + 2,
19062306a36Sopenharmony_ci			.vtotal = 220 + 0 + 2 + 1,
19162306a36Sopenharmony_ci			.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
19262306a36Sopenharmony_ci		},
19362306a36Sopenharmony_ci		.bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
19462306a36Sopenharmony_ci	},
19562306a36Sopenharmony_ci	/*
19662306a36Sopenharmony_ci	 * Sanyo ALR252RGT 240x320 portrait display found on the
19762306a36Sopenharmony_ci	 * Versatile AB IB2 daughterboard for GSM prototyping.
19862306a36Sopenharmony_ci	 */
19962306a36Sopenharmony_ci	{
20062306a36Sopenharmony_ci		.name = "Sanyo ALR252RGT",
20162306a36Sopenharmony_ci		.magic = SYS_CLCD_ID_SANYO_2_5,
20262306a36Sopenharmony_ci		.width_mm = 37,
20362306a36Sopenharmony_ci		.height_mm = 50,
20462306a36Sopenharmony_ci		.mode = {
20562306a36Sopenharmony_ci			.clock = 5400,
20662306a36Sopenharmony_ci			.hdisplay = 240,
20762306a36Sopenharmony_ci			.hsync_start = 240 + 10,
20862306a36Sopenharmony_ci			.hsync_end = 240 + 10 + 10,
20962306a36Sopenharmony_ci			.htotal = 240 + 10 + 10 + 20,
21062306a36Sopenharmony_ci			.vdisplay = 320,
21162306a36Sopenharmony_ci			.vsync_start = 320 + 2,
21262306a36Sopenharmony_ci			.vsync_end = 320 + 2 + 2,
21362306a36Sopenharmony_ci			.vtotal = 320 + 2 + 2 + 2,
21462306a36Sopenharmony_ci			.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
21562306a36Sopenharmony_ci		},
21662306a36Sopenharmony_ci		.bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
21762306a36Sopenharmony_ci		.ib2 = true,
21862306a36Sopenharmony_ci	},
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic inline struct versatile_panel *
22262306a36Sopenharmony_cito_versatile_panel(struct drm_panel *panel)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	return container_of(panel, struct versatile_panel, panel);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic int versatile_panel_disable(struct drm_panel *panel)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct versatile_panel *vpanel = to_versatile_panel(panel);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* If we're on an IB2 daughterboard, turn off display */
23262306a36Sopenharmony_ci	if (vpanel->ib2_map) {
23362306a36Sopenharmony_ci		dev_dbg(vpanel->dev, "disable IB2 display\n");
23462306a36Sopenharmony_ci		regmap_update_bits(vpanel->ib2_map,
23562306a36Sopenharmony_ci				   IB2_CTRL,
23662306a36Sopenharmony_ci				   IB2_CTRL_LCD_MASK,
23762306a36Sopenharmony_ci				   IB2_CTRL_LCD_SD);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	return 0;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic int versatile_panel_enable(struct drm_panel *panel)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct versatile_panel *vpanel = to_versatile_panel(panel);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* If we're on an IB2 daughterboard, turn on display */
24862306a36Sopenharmony_ci	if (vpanel->ib2_map) {
24962306a36Sopenharmony_ci		dev_dbg(vpanel->dev, "enable IB2 display\n");
25062306a36Sopenharmony_ci		regmap_update_bits(vpanel->ib2_map,
25162306a36Sopenharmony_ci				   IB2_CTRL,
25262306a36Sopenharmony_ci				   IB2_CTRL_LCD_MASK,
25362306a36Sopenharmony_ci				   IB2_CTRL_LCD_BL_ON);
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return 0;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int versatile_panel_get_modes(struct drm_panel *panel,
26062306a36Sopenharmony_ci				     struct drm_connector *connector)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct versatile_panel *vpanel = to_versatile_panel(panel);
26362306a36Sopenharmony_ci	struct drm_display_mode *mode;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	connector->display_info.width_mm = vpanel->panel_type->width_mm;
26662306a36Sopenharmony_ci	connector->display_info.height_mm = vpanel->panel_type->height_mm;
26762306a36Sopenharmony_ci	connector->display_info.bus_flags = vpanel->panel_type->bus_flags;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	mode = drm_mode_duplicate(connector->dev, &vpanel->panel_type->mode);
27062306a36Sopenharmony_ci	if (!mode)
27162306a36Sopenharmony_ci		return -ENOMEM;
27262306a36Sopenharmony_ci	drm_mode_set_name(mode);
27362306a36Sopenharmony_ci	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	mode->width_mm = vpanel->panel_type->width_mm;
27662306a36Sopenharmony_ci	mode->height_mm = vpanel->panel_type->height_mm;
27762306a36Sopenharmony_ci	drm_mode_probed_add(connector, mode);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return 1;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic const struct drm_panel_funcs versatile_panel_drm_funcs = {
28362306a36Sopenharmony_ci	.disable = versatile_panel_disable,
28462306a36Sopenharmony_ci	.enable = versatile_panel_enable,
28562306a36Sopenharmony_ci	.get_modes = versatile_panel_get_modes,
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int versatile_panel_probe(struct platform_device *pdev)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
29162306a36Sopenharmony_ci	struct versatile_panel *vpanel;
29262306a36Sopenharmony_ci	struct device *parent;
29362306a36Sopenharmony_ci	struct regmap *map;
29462306a36Sopenharmony_ci	int ret;
29562306a36Sopenharmony_ci	u32 val;
29662306a36Sopenharmony_ci	int i;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	parent = dev->parent;
29962306a36Sopenharmony_ci	if (!parent) {
30062306a36Sopenharmony_ci		dev_err(dev, "no parent for versatile panel\n");
30162306a36Sopenharmony_ci		return -ENODEV;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci	map = syscon_node_to_regmap(parent->of_node);
30462306a36Sopenharmony_ci	if (IS_ERR(map)) {
30562306a36Sopenharmony_ci		dev_err(dev, "no regmap for versatile panel parent\n");
30662306a36Sopenharmony_ci		return PTR_ERR(map);
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	vpanel = devm_kzalloc(dev, sizeof(*vpanel), GFP_KERNEL);
31062306a36Sopenharmony_ci	if (!vpanel)
31162306a36Sopenharmony_ci		return -ENOMEM;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	ret = regmap_read(map, SYS_CLCD, &val);
31462306a36Sopenharmony_ci	if (ret) {
31562306a36Sopenharmony_ci		dev_err(dev, "cannot access syscon regs\n");
31662306a36Sopenharmony_ci		return ret;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	val &= SYS_CLCD_CLCDID_MASK;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) {
32262306a36Sopenharmony_ci		const struct versatile_panel_type *pt;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		pt = &versatile_panels[i];
32562306a36Sopenharmony_ci		if (pt->magic == val) {
32662306a36Sopenharmony_ci			vpanel->panel_type = pt;
32762306a36Sopenharmony_ci			break;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* No panel detected or VGA, let's leave this show */
33262306a36Sopenharmony_ci	if (i == ARRAY_SIZE(versatile_panels)) {
33362306a36Sopenharmony_ci		dev_info(dev, "no panel detected\n");
33462306a36Sopenharmony_ci		return -ENODEV;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	dev_info(dev, "detected: %s\n", vpanel->panel_type->name);
33862306a36Sopenharmony_ci	vpanel->dev = dev;
33962306a36Sopenharmony_ci	vpanel->map = map;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/* Check if the panel is mounted on an IB2 daughterboard */
34262306a36Sopenharmony_ci	if (vpanel->panel_type->ib2) {
34362306a36Sopenharmony_ci		vpanel->ib2_map = syscon_regmap_lookup_by_compatible(
34462306a36Sopenharmony_ci			"arm,versatile-ib2-syscon");
34562306a36Sopenharmony_ci		if (IS_ERR(vpanel->ib2_map))
34662306a36Sopenharmony_ci			vpanel->ib2_map = NULL;
34762306a36Sopenharmony_ci		else
34862306a36Sopenharmony_ci			dev_info(dev, "panel mounted on IB2 daughterboard\n");
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	drm_panel_init(&vpanel->panel, dev, &versatile_panel_drm_funcs,
35262306a36Sopenharmony_ci		       DRM_MODE_CONNECTOR_DPI);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	drm_panel_add(&vpanel->panel);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return 0;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic const struct of_device_id versatile_panel_match[] = {
36062306a36Sopenharmony_ci	{ .compatible = "arm,versatile-tft-panel", },
36162306a36Sopenharmony_ci	{},
36262306a36Sopenharmony_ci};
36362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, versatile_panel_match);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic struct platform_driver versatile_panel_driver = {
36662306a36Sopenharmony_ci	.probe		= versatile_panel_probe,
36762306a36Sopenharmony_ci	.driver		= {
36862306a36Sopenharmony_ci		.name	= "versatile-tft-panel",
36962306a36Sopenharmony_ci		.of_match_table = versatile_panel_match,
37062306a36Sopenharmony_ci	},
37162306a36Sopenharmony_ci};
37262306a36Sopenharmony_cimodule_platform_driver(versatile_panel_driver);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
37562306a36Sopenharmony_ciMODULE_DESCRIPTION("ARM Versatile panel driver");
37662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
377