1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2019, Huaqin Telecom Technology Co., Ltd
5 * Author: Jerry Han <jerry.han.hq@gmail.com>
9 #include <linux/delay.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
14 #include <linux/gpio/consumer.h>
15 #include <linux/regulator/consumer.h>
17 #include <drm/drm_device.h>
18 #include <drm/drm_mipi_dsi.h>
19 #include <drm/drm_modes.h>
20 #include <drm/drm_panel.h>
22 #include <video/mipi_display.h>
30 const struct drm_display_mode *display_mode;
32 unsigned int width_mm;
33 unsigned int height_mm;
35 unsigned long mode_flags;
36 enum mipi_dsi_pixel_format format;
38 const struct panel_cmd *on_cmds;
39 unsigned int on_cmds_num;
43 struct drm_panel base;
44 struct mipi_dsi_device *link;
45 const struct panel_desc *desc;
47 struct gpio_desc *enable_gpio;
48 struct gpio_desc *pp33_gpio;
49 struct gpio_desc *pp18_gpio;
55 static inline struct panel_info *to_panel_info(struct drm_panel *panel)
57 return container_of(panel, struct panel_info, base);
60 static void disable_gpios(struct panel_info *pinfo)
62 gpiod_set_value(pinfo->enable_gpio, 0);
63 gpiod_set_value(pinfo->pp33_gpio, 0);
64 gpiod_set_value(pinfo->pp18_gpio, 0);
67 static int send_mipi_cmds(struct drm_panel *panel, const struct panel_cmd *cmds)
69 struct panel_info *pinfo = to_panel_info(panel);
73 for (i = 0; i < pinfo->desc->on_cmds_num; i++) {
74 err = mipi_dsi_dcs_write_buffer(pinfo->link, &cmds[i],
75 sizeof(struct panel_cmd));
84 static int boe_panel_disable(struct drm_panel *panel)
86 struct panel_info *pinfo = to_panel_info(panel);
92 err = mipi_dsi_dcs_set_display_off(pinfo->link);
94 dev_err(panel->dev, "failed to set display off: %d\n", err);
98 pinfo->enabled = false;
103 static int boe_panel_unprepare(struct drm_panel *panel)
105 struct panel_info *pinfo = to_panel_info(panel);
108 if (!pinfo->prepared)
111 err = mipi_dsi_dcs_set_display_off(pinfo->link);
113 dev_err(panel->dev, "failed to set display off: %d\n", err);
115 err = mipi_dsi_dcs_enter_sleep_mode(pinfo->link);
117 dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
119 /* sleep_mode_delay: 1ms - 2ms */
120 usleep_range(1000, 2000);
122 disable_gpios(pinfo);
124 pinfo->prepared = false;
129 static int boe_panel_prepare(struct drm_panel *panel)
131 struct panel_info *pinfo = to_panel_info(panel);
137 gpiod_set_value(pinfo->pp18_gpio, 1);
139 usleep_range(5000, 6000);
140 gpiod_set_value(pinfo->pp33_gpio, 1);
143 /* T2: 14ms - 15ms */
144 usleep_range(14000, 15000);
145 gpiod_set_value(pinfo->enable_gpio, 1);
148 usleep_range(1000, 2000);
149 gpiod_set_value(pinfo->enable_gpio, 0);
152 usleep_range(1000, 2000);
153 gpiod_set_value(pinfo->enable_gpio, 1);
156 usleep_range(5000, 6000);
159 err = send_mipi_cmds(panel, pinfo->desc->on_cmds);
161 dev_err(panel->dev, "failed to send DCS Init Code: %d\n", err);
165 err = mipi_dsi_dcs_exit_sleep_mode(pinfo->link);
167 dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
171 /* T6: 120ms - 121ms */
172 usleep_range(120000, 121000);
174 err = mipi_dsi_dcs_set_display_on(pinfo->link);
176 dev_err(panel->dev, "failed to set display on: %d\n", err);
180 /* T7: 20ms - 21ms */
181 usleep_range(20000, 21000);
183 pinfo->prepared = true;
188 disable_gpios(pinfo);
192 static int boe_panel_enable(struct drm_panel *panel)
194 struct panel_info *pinfo = to_panel_info(panel);
200 usleep_range(120000, 121000);
202 ret = mipi_dsi_dcs_set_display_on(pinfo->link);
204 dev_err(panel->dev, "failed to set display on: %d\n", ret);
208 pinfo->enabled = true;
213 static int boe_panel_get_modes(struct drm_panel *panel,
214 struct drm_connector *connector)
216 struct panel_info *pinfo = to_panel_info(panel);
217 const struct drm_display_mode *m = pinfo->desc->display_mode;
218 struct drm_display_mode *mode;
220 mode = drm_mode_duplicate(connector->dev, m);
222 dev_err(pinfo->base.dev, "failed to add mode %ux%u@%u\n",
223 m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
227 drm_mode_set_name(mode);
229 drm_mode_probed_add(connector, mode);
231 connector->display_info.width_mm = pinfo->desc->width_mm;
232 connector->display_info.height_mm = pinfo->desc->height_mm;
233 connector->display_info.bpc = pinfo->desc->bpc;
238 static const struct drm_panel_funcs panel_funcs = {
239 .disable = boe_panel_disable,
240 .unprepare = boe_panel_unprepare,
241 .prepare = boe_panel_prepare,
242 .enable = boe_panel_enable,
243 .get_modes = boe_panel_get_modes,
246 static const struct drm_display_mode default_display_mode = {
249 .hsync_start = 1200 + 80,
250 .hsync_end = 1200 + 80 + 60,
251 .htotal = 1200 + 80 + 60 + 24,
253 .vsync_start = 1920 + 10,
254 .vsync_end = 1920 + 10 + 14,
255 .vtotal = 1920 + 10 + 14 + 4,
259 static const struct panel_cmd boe_himax8279d8p_on_cmds[] = {
522 static const struct panel_desc boe_himax8279d8p_panel_desc = {
523 .display_mode = &default_display_mode,
527 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
528 MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
529 .format = MIPI_DSI_FMT_RGB888,
531 .on_cmds = boe_himax8279d8p_on_cmds,
536 static const struct panel_cmd boe_himax8279d10p_on_cmds[] = {
822 static const struct panel_desc boe_himax8279d10p_panel_desc = {
823 .display_mode = &default_display_mode,
827 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
828 MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
829 .format = MIPI_DSI_FMT_RGB888,
831 .on_cmds = boe_himax8279d10p_on_cmds,
835 static const struct of_device_id panel_of_match[] = {
837 .compatible = "boe,himax8279d8p",
838 .data = &boe_himax8279d8p_panel_desc,
841 .compatible = "boe,himax8279d10p",
842 .data = &boe_himax8279d10p_panel_desc,
848 MODULE_DEVICE_TABLE(of, panel_of_match);
850 static int panel_add(struct panel_info *pinfo)
852 struct device *dev = &pinfo->link->dev;
855 pinfo->pp18_gpio = devm_gpiod_get(dev, "pp18", GPIOD_OUT_HIGH);
856 if (IS_ERR(pinfo->pp18_gpio)) {
857 ret = PTR_ERR(pinfo->pp18_gpio);
858 if (ret != -EPROBE_DEFER)
859 dev_err(dev, "failed to get pp18 gpio: %d\n", ret);
863 pinfo->pp33_gpio = devm_gpiod_get(dev, "pp33", GPIOD_OUT_HIGH);
864 if (IS_ERR(pinfo->pp33_gpio)) {
865 ret = PTR_ERR(pinfo->pp33_gpio);
866 if (ret != -EPROBE_DEFER)
867 dev_err(dev, "failed to get pp33 gpio: %d\n", ret);
871 pinfo->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
872 if (IS_ERR(pinfo->enable_gpio)) {
873 ret = PTR_ERR(pinfo->enable_gpio);
874 if (ret != -EPROBE_DEFER)
875 dev_err(dev, "failed to get enable gpio: %d\n", ret);
879 drm_panel_init(&pinfo->base, dev, &panel_funcs,
880 DRM_MODE_CONNECTOR_DSI);
882 ret = drm_panel_of_backlight(&pinfo->base);
886 drm_panel_add(&pinfo->base);
891 static int panel_probe(struct mipi_dsi_device *dsi)
893 struct panel_info *pinfo;
894 const struct panel_desc *desc;
897 pinfo = devm_kzalloc(&dsi->dev, sizeof(*pinfo), GFP_KERNEL);
901 desc = of_device_get_match_data(&dsi->dev);
902 dsi->mode_flags = desc->mode_flags;
903 dsi->format = desc->format;
904 dsi->lanes = desc->lanes;
908 mipi_dsi_set_drvdata(dsi, pinfo);
910 err = panel_add(pinfo);
914 err = mipi_dsi_attach(dsi);
916 drm_panel_remove(&pinfo->base);
921 static void panel_remove(struct mipi_dsi_device *dsi)
923 struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
926 err = boe_panel_disable(&pinfo->base);
928 dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
930 err = boe_panel_unprepare(&pinfo->base);
932 dev_err(&dsi->dev, "failed to unprepare panel: %d\n", err);
934 err = mipi_dsi_detach(dsi);
936 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
938 drm_panel_remove(&pinfo->base);
941 static void panel_shutdown(struct mipi_dsi_device *dsi)
943 struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
945 boe_panel_disable(&pinfo->base);
946 boe_panel_unprepare(&pinfo->base);
949 static struct mipi_dsi_driver panel_driver = {
951 .name = "panel-boe-himax8279d",
952 .of_match_table = panel_of_match,
954 .probe = panel_probe,
955 .remove = panel_remove,
956 .shutdown = panel_shutdown,
958 module_mipi_dsi_driver(panel_driver);
960 MODULE_AUTHOR("Jerry Han <jerry.han.hq@gmail.com>");
961 MODULE_DESCRIPTION("Boe Himax8279d driver");
962 MODULE_LICENSE("GPL v2");