1 // SPDX-License-Identifier: GPL-2.0+
3 * MIPI-DSI Samsung s6d16d0 panel driver. This is a 864x480
4 * AMOLED panel with a command-only DSI interface.
7 #include <drm/drm_modes.h>
8 #include <drm/drm_mipi_dsi.h>
9 #include <drm/drm_panel.h>
11 #include <linux/gpio/consumer.h>
12 #include <linux/regulator/consumer.h>
13 #include <linux/delay.h>
14 #include <linux/of_device.h>
15 #include <linux/module.h>
19 struct drm_panel panel
;
20 struct regulator
*supply
;
21 struct gpio_desc
*reset_gpio
;
25 * The timings are not very helpful as the display is used in
28 static const struct drm_display_mode samsung_s6d16d0_mode
= {
29 /* HS clock, (htotal*vtotal*vrefresh)/1000 */
32 .hsync_start
= 864 + 154,
33 .hsync_end
= 864 + 154 + 16,
34 .htotal
= 864 + 154 + 16 + 32,
36 .vsync_start
= 480 + 1,
37 .vsync_end
= 480 + 1 + 1,
38 .vtotal
= 480 + 1 + 1 + 1,
43 static inline struct s6d16d0
*panel_to_s6d16d0(struct drm_panel
*panel
)
45 return container_of(panel
, struct s6d16d0
, panel
);
48 static int s6d16d0_unprepare(struct drm_panel
*panel
)
50 struct s6d16d0
*s6
= panel_to_s6d16d0(panel
);
51 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(s6
->dev
);
54 /* Enter sleep mode */
55 ret
= mipi_dsi_dcs_enter_sleep_mode(dsi
);
57 dev_err(s6
->dev
, "failed to enter sleep mode (%d)\n", ret
);
62 gpiod_set_value_cansleep(s6
->reset_gpio
, 1);
63 regulator_disable(s6
->supply
);
68 static int s6d16d0_prepare(struct drm_panel
*panel
)
70 struct s6d16d0
*s6
= panel_to_s6d16d0(panel
);
71 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(s6
->dev
);
74 ret
= regulator_enable(s6
->supply
);
76 dev_err(s6
->dev
, "failed to enable supply (%d)\n", ret
);
81 gpiod_set_value_cansleep(s6
->reset_gpio
, 1);
84 gpiod_set_value_cansleep(s6
->reset_gpio
, 0);
87 /* Enabe tearing mode: send TE (tearing effect) at VBLANK */
88 ret
= mipi_dsi_dcs_set_tear_on(dsi
,
89 MIPI_DSI_DCS_TEAR_MODE_VBLANK
);
91 dev_err(s6
->dev
, "failed to enable vblank TE (%d)\n", ret
);
94 /* Exit sleep mode and power on */
95 ret
= mipi_dsi_dcs_exit_sleep_mode(dsi
);
97 dev_err(s6
->dev
, "failed to exit sleep mode (%d)\n", ret
);
104 static int s6d16d0_enable(struct drm_panel
*panel
)
106 struct s6d16d0
*s6
= panel_to_s6d16d0(panel
);
107 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(s6
->dev
);
110 ret
= mipi_dsi_dcs_set_display_on(dsi
);
112 dev_err(s6
->dev
, "failed to turn display on (%d)\n", ret
);
119 static int s6d16d0_disable(struct drm_panel
*panel
)
121 struct s6d16d0
*s6
= panel_to_s6d16d0(panel
);
122 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(s6
->dev
);
125 ret
= mipi_dsi_dcs_set_display_off(dsi
);
127 dev_err(s6
->dev
, "failed to turn display off (%d)\n", ret
);
134 static int s6d16d0_get_modes(struct drm_panel
*panel
,
135 struct drm_connector
*connector
)
137 struct drm_display_mode
*mode
;
139 mode
= drm_mode_duplicate(connector
->dev
, &samsung_s6d16d0_mode
);
141 dev_err(panel
->dev
, "bad mode or failed to add mode\n");
144 drm_mode_set_name(mode
);
145 mode
->type
= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
;
147 connector
->display_info
.width_mm
= mode
->width_mm
;
148 connector
->display_info
.height_mm
= mode
->height_mm
;
150 drm_mode_probed_add(connector
, mode
);
152 return 1; /* Number of modes */
155 static const struct drm_panel_funcs s6d16d0_drm_funcs
= {
156 .disable
= s6d16d0_disable
,
157 .unprepare
= s6d16d0_unprepare
,
158 .prepare
= s6d16d0_prepare
,
159 .enable
= s6d16d0_enable
,
160 .get_modes
= s6d16d0_get_modes
,
163 static int s6d16d0_probe(struct mipi_dsi_device
*dsi
)
165 struct device
*dev
= &dsi
->dev
;
169 s6
= devm_kzalloc(dev
, sizeof(struct s6d16d0
), GFP_KERNEL
);
173 mipi_dsi_set_drvdata(dsi
, s6
);
177 dsi
->format
= MIPI_DSI_FMT_RGB888
;
178 dsi
->hs_rate
= 420160000;
179 dsi
->lp_rate
= 19200000;
181 * This display uses command mode so no MIPI_DSI_MODE_VIDEO
182 * or MIPI_DSI_MODE_VIDEO_SYNC_PULSE
184 * As we only send commands we do not need to be continuously
188 MIPI_DSI_CLOCK_NON_CONTINUOUS
|
189 MIPI_DSI_MODE_EOT_PACKET
;
191 s6
->supply
= devm_regulator_get(dev
, "vdd1");
192 if (IS_ERR(s6
->supply
))
193 return PTR_ERR(s6
->supply
);
195 /* This asserts RESET by default */
196 s6
->reset_gpio
= devm_gpiod_get_optional(dev
, "reset",
198 if (IS_ERR(s6
->reset_gpio
)) {
199 ret
= PTR_ERR(s6
->reset_gpio
);
200 if (ret
!= -EPROBE_DEFER
)
201 dev_err(dev
, "failed to request GPIO (%d)\n", ret
);
205 drm_panel_init(&s6
->panel
, dev
, &s6d16d0_drm_funcs
,
206 DRM_MODE_CONNECTOR_DSI
);
208 drm_panel_add(&s6
->panel
);
210 ret
= mipi_dsi_attach(dsi
);
212 drm_panel_remove(&s6
->panel
);
217 static int s6d16d0_remove(struct mipi_dsi_device
*dsi
)
219 struct s6d16d0
*s6
= mipi_dsi_get_drvdata(dsi
);
221 mipi_dsi_detach(dsi
);
222 drm_panel_remove(&s6
->panel
);
227 static const struct of_device_id s6d16d0_of_match
[] = {
228 { .compatible
= "samsung,s6d16d0" },
231 MODULE_DEVICE_TABLE(of
, s6d16d0_of_match
);
233 static struct mipi_dsi_driver s6d16d0_driver
= {
234 .probe
= s6d16d0_probe
,
235 .remove
= s6d16d0_remove
,
237 .name
= "panel-samsung-s6d16d0",
238 .of_match_table
= s6d16d0_of_match
,
241 module_mipi_dsi_driver(s6d16d0_driver
);
243 MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>");
244 MODULE_DESCRIPTION("MIPI-DSI s6d16d0 Panel Driver");
245 MODULE_LICENSE("GPL v2");