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");
+