Add modified Startek panel driver

-Remove syscon DSI references in mtk_dsi.c
-Register backlight using DT
-Clean up panel enable/disable functions
-Change timing structs to be more similar to other MTK panel drivers
-Changes in DT also include focaltech touchscreen

Change-Id: I163113da7ef5ffdcb1c2af024da263b2c26fe295
diff --git a/arch/arm64/boot/dts/mediatek/mt8167-coral.dts b/arch/arm64/boot/dts/mediatek/mt8167-coral.dts
index cd48899..c124851 100644
--- a/arch/arm64/boot/dts/mediatek/mt8167-coral.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8167-coral.dts
@@ -85,6 +85,16 @@
 		pinctrl-0 = <&aud_pins_default>;
 		mediatek,jack-detect-gpio = <&pio 40 0>;
 	};
+
+	backlight: backlight_panel0 {
+		compatible = "pwm-backlight";
+		pwms = <&pwm 0 1000000>;
+		power-supply = <&usb0_vbus>;
+		enable-gpios = <&pio 25 0>;
+		brightness-levels = <0 4 8 16 32 64 128 255>;
+		default-brightness-level = <6>;
+		status = "okay";
+	};
 };
 
 &afe {
@@ -116,6 +126,7 @@
 };
 
 &mt6392_vcamaf_reg {
+	regulator-min-microvolt = <3300000>;
 	regulator-always-on;
 };
 
@@ -124,6 +135,19 @@
 	regulator-always-on;
 };
 
+&mt6392_vgp2_reg {
+	regulator-min-microvolt = <3300000>;
+	regulator-always-on;
+};
+
+&mt6392_vmch_reg {
+	regulator-min-microvolt = <3300000>;
+};
+
+&mt6392_vmc_reg {
+	regulator-min-microvolt = <3300000>;
+};
+
 &uart0 {
 	status = "okay";
 };
@@ -165,6 +189,34 @@
 		};
 	};
 
+	panel_pins: panel_pins {
+		pins1 {
+			pinmux = <MT8167_PIN_66_LCM_RST__FUNC_GPIO66>;
+		//					 <MT8167_PIN_67_DSI_TE__FUNC_GPIO67>;
+			output-high;
+		};
+	};
+
+	ts_pin_nint: ts_pin_nint {
+	 pins1 {
+		pinmux = <MT8167_PIN_16_EINT16__FUNC_GPIO16>;
+	 };
+	};
+
+	ts_pin_nrst: ts_pin_nrst {
+	 pins1 {
+		pinmux = <MT8167_PIN_21_EINT21__FUNC_GPIO21>;
+		output-high;
+	 };
+	};
+
+	pwm0_pin_default: pwm0_pin_default {
+		pins1 {
+			pinmux = <MT8167_PIN_25_EINT25__FUNC_PWM_B>;
+		};
+	};
+
+
 	mmc0_pins_default: mmc0default {
 		pins_cmd_dat {
 			pinmux = <MT8167_PIN_120_MSDC0_DAT0__FUNC_MSDC0_DAT0>,
@@ -330,44 +382,6 @@
 		};
 	};
 
-	mmc2_pins_default: mmc2default {
-		pins_cmd_dat {
-			pinmux = <MT8167_PIN_70_MSDC2_DAT0__FUNC_MSDC2_DAT0>,
-				<MT8167_PIN_71_MSDC2_DAT1__FUNC_MSDC2_DAT1>,
-				<MT8167_PIN_72_MSDC2_DAT2__FUNC_MSDC2_DAT2>,
-				<MT8167_PIN_73_MSDC2_DAT3__FUNC_MSDC2_DAT3>,
-				<MT8167_PIN_68_MSDC2_CMD__FUNC_MSDC2_CMD>;
-			input-enable;
-			drive-strength = <MTK_DRIVE_6mA>;
-			bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
-		};
-
-		pins_clk {
-			pinmux = <MT8167_PIN_69_MSDC2_CLK__FUNC_MSDC2_CLK>;
-			drive-strength = <MTK_DRIVE_6mA>;
-			bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
-		};
-	};
-
-	mmc2_pins_uhs: mmc2@0 {
-		pins_cmd_dat {
-			pinmux = <MT8167_PIN_70_MSDC2_DAT0__FUNC_MSDC2_DAT0>,
-				<MT8167_PIN_71_MSDC2_DAT1__FUNC_MSDC2_DAT1>,
-				<MT8167_PIN_72_MSDC2_DAT2__FUNC_MSDC2_DAT2>,
-				<MT8167_PIN_73_MSDC2_DAT3__FUNC_MSDC2_DAT3>,
-				<MT8167_PIN_68_MSDC2_CMD__FUNC_MSDC2_CMD>;
-			input-enable;
-			drive-strength = <MTK_DRIVE_6mA>;
-			bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
-		};
-
-		pins_clk {
-			pinmux = <MT8167_PIN_69_MSDC2_CLK__FUNC_MSDC2_CLK>;
-			drive-strength = <MTK_DRIVE_8mA>;
-			bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
-		};
-	};
-
 	hdmi_pins_default: hdmi_pins_default {
 	};
 
@@ -427,6 +441,18 @@
 	pinctrl-0 = <&i2c2_pins_a>;
 	sda-gpios = <&pio 60 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
 	scl-gpios = <&pio 61 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+
+	focaltech@38 {
+	 status = "okay";
+	 compatible = "focaltech,fts";
+	 reg = <0x38>;
+	 pinctrl-0 = <&ts_pin_nint>;
+	 focaltech,panel-type = <FT6236>;
+	 focaltech,reset-gpio = <&pio 21 GPIO_ACTIVE_HIGH>;
+	 focaltech,irq-gpio = <&pio 16 GPIO_ACTIVE_HIGH>;
+	 focaltech,max-touch-number = <5>;
+	 focaltech,display-coords = <0 0 320 480>;
+	};
 };
 
 &mmc0 {
@@ -488,45 +514,54 @@
 	vqmmc-supply = <&mt6392_vmc_reg>;
 };
 
-&dpi1 {
-	status = "okay";
-	ddc-i2c-bus = <&hdmiddc>;
 
-	port {
-		hdmi_connector_in: endpoint@0 {
-			remote-endpoint = <&hdmi_out>;
-		};
-	};
+&pwm {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pwm0_pin_default>;
+	#pwm-cells = <2>;
 };
 
-&hdmi_phy {
+&mipi_tx {
 	status = "okay";
 };
 
-&cec {
+&dsi {
 	status = "okay";
-};
-
-&hdmi {
-	pinctrl-names = "default", "hdmi_hpd";
-	pinctrl-0 = <&hdmi_pins_default>;
-	pinctrl-1 = <&hdmi_pins_hpd>;
-	status = "okay";
+	#address-cells = <1>;
+	#size-cells = <0>;
+	//mediatek,syscon-dsi = <&mmsys 0x140>;
 
 	ports {
-		#address-cells = <1>;
-		#size-cells = <0>;
+		port@0 {
+			reg = <0>;
 
-		port@1 {
-			reg = <1>;
+			dsi_out: endpoint {
+				remote-endpoint = <&panel_in>;
+			};
+		};
+	};
 
-			hdmi_out: endpoint {
-				remote-endpoint = <&hdmi_connector_in>;
+	panel@0 {
+		compatible = "startek,ili9488";
+		reg = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&panel_pins>;
+		reset-gpio = <&pio 66 GPIO_ACTIVE_HIGH>;
+		dsi-lanes = <1>;
+		panel-width-mm = <49>;
+		panel-height-mm = <74>;
+		backlight = <&backlight>;
+		status = "okay";
+		port {
+			panel_in: endpoint {
+				remote-endpoint = <&dsi_out>;
 			};
 		};
 	};
 };
 
+
 &usb0 {
 	status = "okay";
 	dr_mode = "otg";
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index aa32554..7d2cbc2 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -147,7 +147,7 @@
 #define DATA_0				(0xff << 16)
 #define DATA_1				(0xff << 24)
 
-#define MMSYS_SW_RST_DSI_B BIT(25)
+//#define MMSYS_SW_RST_DSI_B BIT(25)
 
 #define NS_TO_CYCLE(n, c)    ((n) / (c) + (((n) % (c)) ? 1 : 0))
 
@@ -193,7 +193,7 @@
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
 	struct phy *phy;
-	struct regmap *mmsys_sw_rst_b;
+	//struct regmap *mmsys_sw_rst_b;
 	u32 sw_rst_b;
 
 	void __iomem *regs;
@@ -286,7 +286,7 @@
 {
 	mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_EN, 0);
 }
-
+/*
 static void mtk_dsi_reset_all(struct mtk_dsi *dsi)
 {
 	if (!dsi->mmsys_sw_rst_b)
@@ -299,7 +299,7 @@
 	regmap_update_bits(dsi->mmsys_sw_rst_b, dsi->sw_rst_b,
 			   MMSYS_SW_RST_DSI_B, MMSYS_SW_RST_DSI_B);
 }
-
+*/
 static void mtk_dsi_reset_engine(struct mtk_dsi *dsi)
 {
 	mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_RESET, DSI_RESET);
@@ -664,6 +664,9 @@
 	dsi->data_rate = DIV_ROUND_UP_ULL(dsi->vm.pixelclock * bit_per_pixel,
 					  dsi->lanes);
 
+	pr_info("pixel clock: %d\n", dsi->vm.pixelclock);
+	pr_info("bit per pixel: %d\n", bit_per_pixel);
+	pr_info("DSI data rate: %d\n", dsi->data_rate);
 	ret = clk_set_rate(dsi->hs_clk, dsi->data_rate);
 	if (ret < 0) {
 		dev_err(dev, "Failed to set data rate: %d\n", ret);
@@ -945,7 +948,7 @@
 			goto err_encoder_cleanup;
 	}
 
-	mtk_dsi_reset_all(dsi);
+//	mtk_dsi_reset_all(dsi);
 
 	return 0;
 
@@ -1260,7 +1263,7 @@
 		dev_err(dev, "Failed to get MIPI-DPHY: %d\n", ret);
 		goto err_unregister_host;
 	}
-
+/*
 	regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
 						 "mediatek,syscon-dsi");
 	ret = of_property_read_u32_index(dev->of_node, "mediatek,syscon-dsi", 1,
@@ -1275,7 +1278,7 @@
 	} else {
 		dsi->mmsys_sw_rst_b = regmap;
 	}
-
+*/
 	comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DSI);
 	if (comp_id < 0) {
 		dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 429d697..3dc5dc4 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -125,6 +125,13 @@
 	  Pi 7" Touchscreen modified for use with the pumpkin boards.
 	  To compile this driver as a module, choose M here.
 
+config DRM_PANEL_STARTEK_ILI9488
+  tristate "Startek Panel"
+  depends on OF
+  depends on DRM_MIPI_DSI
+  help
+     Say Y here if you want to enable the Startek Panel.
+
 config DRM_PANEL_RAYDIUM_RM68200
 	tristate "Raydium RM68200 720x1280 DSI video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index e4cd72b6..6bf27b4 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -23,3 +23,4 @@
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
 obj-$(CONFIG_DRM_PANEL_TPV_OTM1901A) += panel-tpv-otm1901a.o
 obj-$(CONFIG_DRM_PANEL_TRULY_R63350A) += panel-truly-r63350a.o
+obj-$(CONFIG_DRM_PANEL_STARTEK_ILI9488) += panel-startek-ili9488.o
diff --git a/drivers/gpu/drm/panel/panel-startek-ili9488.c b/drivers/gpu/drm/panel/panel-startek-ili9488.c
new file mode 100644
index 0000000..32c07b2
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-startek-ili9488.c
@@ -0,0 +1,389 @@
+#include <drm/drm_mipi_dsi.h> 
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pwm.h>
+#include <uapi/linux/media-bus-format.h>
+#include <video/mipi_display.h>
+#include <video/videomode.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+
+struct startek_panel {
+	struct drm_panel panel;
+	struct mipi_dsi_device *dsi;
+	struct gpio_desc *reset;
+	struct backlight_device *backlight;
+};
+
+static const struct drm_display_mode startek_default_timing = {
+	.clock = 26000000 / 1000,
+	.hdisplay = 320,
+	.hsync_start = 320 + 170,
+	.hsync_end = 320 + 170 + 50,
+	.htotal = 320 + 170 + 50 + 50,
+	.vdisplay = 480,
+	.vsync_start = 480 + 2,
+	.vsync_end = 480 + 2 + 1,
+	.vtotal = 480 + 2 + 1 + 1,
+	.vrefresh = 60,
+};
+
+#define MAX_PARA_NUM 16
+struct dcs_command {
+	unsigned char cmd;
+	unsigned char para_size;
+	unsigned char para[MAX_PARA_NUM];
+	unsigned int sleep;
+};
+
+static const struct dcs_command g_startek_init[] = {
+		// PGAMCTRL
+		{.cmd = 0xe0,
+		 .para_size = 15,
+		 .para = {0x00, 0x04, 0x0e, 0x08, 0x17, 0x0a, 0x40, 0x79, 0x4d, 0x07, 0x0e,
+							0x0a, 0x1a, 0x1d, 0x0f}},
+
+		// NGAMCTRL
+		{.cmd = 0xe1,
+		 .para_size = 15,
+		 .para = {0x00, 0x1b, 0x1f, 0x02, 0x10, 0x05, 0x32, 0x34, 0x43, 0x02, 0x0a,
+							0x09, 0x33, 0x37, 0x0f}},
+
+		// Power Control 1
+		{.cmd = 0xc0, .para_size = 2, .para = {0x18, 0x16}},
+
+		// Power Control 2
+		{.cmd = 0xc1, .para_size = 1, .para = {0x41}},
+
+		// VCOM Control
+		{.cmd = 0xc5, .para_size = 3, .para = {0x00, 0x1e, 0x80}},
+
+		// Interface Mode Control
+		{.cmd = 0x3a, .para_size = 1, .para = {0x77}},
+
+		// Frame rate 70HZ
+		{.cmd = 0xb1, .para_size = 1, .para = {0xb0}},
+
+		// Display Inversion Control
+		{.cmd = 0xb4, .para_size = 1, .para = {0x02}},
+
+		// Blanking Porch Control
+		{.cmd = 0xb5, .para_size = 4, .para = {0x02, 0x02, 0xaa, 0x64}},
+
+		// Set Image Function
+		{.cmd = 0xe9, .para_size = 1, .para = {0x01}},
+
+		// Adjust Control 3
+		{.cmd = 0xf7, .para_size = 4, .para = {0xa9, 0x51, 0x2c, 0x82}},
+
+		// Display Function Control
+		{.cmd = 0xb6, .para_size = 3, .para = {0x02, 0x02, 0x3b}},
+
+		// Column Address Set
+		{.cmd = 0x2a, .para_size = 4, .para = {0x00, 0x00, 0x01, 0x3f}},
+
+		// Page Address Set
+		{.cmd = 0x2b, .para_size = 4, .para = {0x00, 0x00, 0x01, 0xdf}},
+
+		// Entry Mode Set
+		{.cmd = 0xb7, .para_size = 1, .para = {0xc6}},
+
+		// Interface Mode Control
+		{.cmd = 0xb0, .para_size = 1, .para = {0x00}},
+
+};
+
+// Seems 0x36 only takes effect after exit sleep.
+// And if init all registers after exit sleep, other issue.
+// So we seperate 2 init phases.
+
+static const struct dcs_command g_startek_later_init[] = {
+		// Memory Access Control
+		{.cmd = 0x36, .para_size = 1, .para = {0x48}},
+};
+
+static int startek_init(struct mipi_dsi_device *dsi,
+												const struct dcs_command *init_command, int num) {
+	dev_err(&dsi->dev, "init");
+	int i;
+	int ret;
+
+	dev_info(&dsi->dev, "%s(), command num %d\n", __func__, num);
+	for (i = 0; i < num; i++) {
+		struct dcs_command *cmd = &init_command[i];
+
+		ret = mipi_dsi_dcs_write(dsi, cmd->cmd, cmd->para, cmd->para_size);
+		if (ret < 0) {
+			dev_err(&dsi->dev, "%s(), command 0x%x, ret %d\n", __func__, cmd->cmd,
+							ret);
+			return ret;
+		}
+
+		if (cmd->sleep)
+			msleep(cmd->sleep);
+	}
+
+	return 0;
+}
+
+static int startek_panel_get_modes(struct drm_panel *panel)
+{
+
+	struct drm_display_mode *mode;
+	static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+	int ret;
+
+	mode = drm_mode_duplicate(panel->drm, &startek_default_timing);
+	if (!mode) {
+		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+				startek_default_timing.hdisplay, startek_default_timing.vdisplay,
+				startek_default_timing.vrefresh);
+		return -ENOMEM;
+	}
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(panel->connector, mode);
+
+	panel->connector->display_info.bpc = 8;
+	panel->connector->display_info.height_mm = 49;
+	panel->connector->display_info.width_mm = 74;
+
+	panel->connector->display_info.bus_flags =
+			DRM_BUS_FLAG_PIXDATA_POSEDGE | DRM_BUS_FLAG_DE_HIGH;
+
+	ret = drm_display_info_set_bus_formats(&panel->connector->display_info,
+																				 &bus_format, 1);
+
+	if (ret) {
+		return 0;
+	}
+
+	return 1;
+}
+
+static int startek_panel_prepare(struct drm_panel *panel) {
+	pr_err("prepare");
+	int ret = 0;
+	struct startek_panel *startek =
+			container_of(panel, struct startek_panel, panel);
+	struct mipi_dsi_device *dsi = startek->dsi;
+	uint8_t ctrl = 0x2C;
+	uint8_t powersave = 0x0;
+	uint16_t min_bright = 0x80;
+
+	gpiod_set_value(startek->reset, 1);
+	msleep(1);
+	gpiod_set_value(startek->reset, 0);
+	msleep(10);
+	gpiod_set_value(startek->reset, 1);
+	msleep(120);
+
+	ret = startek_init(dsi, g_startek_init, ARRAY_SIZE(g_startek_init));
+	if (ret < 0) {
+		dev_err(&dsi->dev, "%s(), startek_init failed, ret %d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x0ff);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to set brightness: %d", ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, &ctrl,
+													 sizeof(ctrl));
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to set control: %d", ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE, &powersave,
+													 sizeof(powersave));
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to set powersave: %d", ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, &min_bright,
+													 sizeof(min_bright));
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to set min_bright: %d", ret);
+		return ret;
+	}
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to exit sleep mode: %d", ret);
+		return ret;
+	}
+	msleep(120);
+
+	startek_init(dsi, g_startek_later_init, ARRAY_SIZE(g_startek_later_init));
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to turn on display: %d", ret);
+		return ret;
+	}
+
+	dev_info(&dsi->dev, "prepare ok\n");
+
+	return 0;
+}
+
+static int startek_panel_unprepare(struct drm_panel *panel) {
+	struct startek_panel *startek =
+			container_of(panel, struct startek_panel, panel);
+	struct mipi_dsi_device *dsi = startek->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Could not set display off: %d", ret);
+		return ret;
+	}
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Could not enter sleep mode: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int startek_panel_enable(struct drm_panel *panel)
+{
+	struct startek_panel *startek =
+			container_of(panel, struct startek_panel, panel);
+
+	backlight_enable(startek->backlight);
+
+	return 0;
+}
+
+static int startek_panel_disable(struct drm_panel *panel)
+{
+	struct startek_panel *startek =
+			container_of(panel, struct startek_panel, panel);
+
+	backlight_disable(startek->backlight);
+
+	return 0;
+}
+
+static const struct drm_panel_funcs startek_panel_funcs = {
+		.get_modes = startek_panel_get_modes,
+		.enable = startek_panel_enable,
+		.disable = startek_panel_disable,
+		.prepare = startek_panel_prepare,
+		.unprepare = startek_panel_unprepare,
+};
+
+static int startek_panel_probe(struct mipi_dsi_device *dsi) {
+	dev_err(&dsi->dev, "probing");
+	
+	struct startek_panel *startek;
+	int ret;
+
+	startek = devm_kzalloc(&dsi->dev, sizeof(*startek), GFP_KERNEL);
+	if (!startek)
+		return -ENOMEM;
+
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =
+		MIPI_DSI_MODE_VIDEO_HSE |
+		MIPI_DSI_MODE_VIDEO |
+		MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	ret = of_property_read_u32(dsi->dev.of_node, "dsi-lanes", &dsi->lanes);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to get dsi-lanes property: %d", ret);
+		return ret;
+	}
+
+	mipi_dsi_set_drvdata(dsi, startek);
+
+	startek->dsi = dsi;
+
+	startek->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(startek->reset)) {
+		ret = PTR_ERR(startek->reset);
+		dev_err(&dsi->dev, "Failed to get reset gpio: %d\n", ret);
+		return ret;
+	}
+
+	startek->backlight = devm_of_find_backlight(&startek->dsi->dev);
+	if (IS_ERR(startek->backlight)) {
+		ret = PTR_ERR(startek->backlight);
+		dev_err(&dsi->dev, "Failed to get backlight: %d\n", ret);
+		return ret;
+	}
+
+	drm_panel_init(&startek->panel);
+	startek->panel.funcs = &startek_panel_funcs;
+	startek->panel.dev = &startek->dsi->dev;
+
+	ret = drm_panel_add(&startek->panel);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to add panel: %d\n", ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Unable to mipi_dsi_attach! %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int startek_panel_remove(struct mipi_dsi_device *dsi) {
+
+	struct startek_panel *startek = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	gpiod_set_value(startek->reset, 1);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "Failed to mipi_dsi_detach! %d\n", ret);
+
+	if (startek->panel.dev)
+		drm_panel_remove(&startek->panel);
+
+	return 0;
+}
+
+static const struct of_device_id startek_of_match[] = {
+		{
+				.compatible = "startek,ili9488",
+		},
+		{}};
+MODULE_DEVICE_TABLE(of, startek_of_match);
+
+static struct mipi_dsi_driver startek_panel_driver = {
+		.driver =
+				{
+						.name = "panel-startek-ili9488", .of_match_table = startek_of_match,
+				},
+		.probe = startek_panel_probe,
+		.remove = startek_panel_remove,
+};
+module_mipi_dsi_driver(startek_panel_driver);
+
+MODULE_AUTHOR("Fang Hui <hui.fang@nxp.com>");
+MODULE_DESCRIPTION("Startek Display panel");
+MODULE_LICENSE("GPL v2");
+