0001
0002
0003
0004
0005
0006
0007 #include <linux/delay.h>
0008 #include <linux/gpio/consumer.h>
0009 #include <linux/module.h>
0010 #include <linux/of.h>
0011 #include <linux/regulator/consumer.h>
0012
0013 #include <video/mipi_display.h>
0014
0015 #include <drm/drm_crtc.h>
0016 #include <drm/drm_device.h>
0017 #include <drm/drm_mipi_dsi.h>
0018 #include <drm/drm_modes.h>
0019 #include <drm/drm_panel.h>
0020
0021 struct tdo_tl070wsh30_panel {
0022 struct drm_panel base;
0023 struct mipi_dsi_device *link;
0024
0025 struct regulator *supply;
0026 struct gpio_desc *reset_gpio;
0027
0028 bool prepared;
0029 };
0030
0031 static inline
0032 struct tdo_tl070wsh30_panel *to_tdo_tl070wsh30_panel(struct drm_panel *panel)
0033 {
0034 return container_of(panel, struct tdo_tl070wsh30_panel, base);
0035 }
0036
0037 static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel)
0038 {
0039 struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
0040 int err;
0041
0042 if (tdo_tl070wsh30->prepared)
0043 return 0;
0044
0045 err = regulator_enable(tdo_tl070wsh30->supply);
0046 if (err < 0)
0047 return err;
0048
0049 usleep_range(10000, 11000);
0050
0051 gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 1);
0052
0053 usleep_range(10000, 11000);
0054
0055 gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 0);
0056
0057 msleep(200);
0058
0059 err = mipi_dsi_dcs_exit_sleep_mode(tdo_tl070wsh30->link);
0060 if (err < 0) {
0061 dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
0062 regulator_disable(tdo_tl070wsh30->supply);
0063 return err;
0064 }
0065
0066 msleep(200);
0067
0068 err = mipi_dsi_dcs_set_display_on(tdo_tl070wsh30->link);
0069 if (err < 0) {
0070 dev_err(panel->dev, "failed to set display on: %d\n", err);
0071 regulator_disable(tdo_tl070wsh30->supply);
0072 return err;
0073 }
0074
0075 msleep(20);
0076
0077 tdo_tl070wsh30->prepared = true;
0078
0079 return 0;
0080 }
0081
0082 static int tdo_tl070wsh30_panel_unprepare(struct drm_panel *panel)
0083 {
0084 struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
0085 int err;
0086
0087 if (!tdo_tl070wsh30->prepared)
0088 return 0;
0089
0090 err = mipi_dsi_dcs_set_display_off(tdo_tl070wsh30->link);
0091 if (err < 0)
0092 dev_err(panel->dev, "failed to set display off: %d\n", err);
0093
0094 usleep_range(10000, 11000);
0095
0096 err = mipi_dsi_dcs_enter_sleep_mode(tdo_tl070wsh30->link);
0097 if (err < 0) {
0098 dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
0099 return err;
0100 }
0101
0102 usleep_range(10000, 11000);
0103
0104 regulator_disable(tdo_tl070wsh30->supply);
0105
0106 tdo_tl070wsh30->prepared = false;
0107
0108 return 0;
0109 }
0110
0111 static const struct drm_display_mode default_mode = {
0112 .clock = 47250,
0113 .hdisplay = 1024,
0114 .hsync_start = 1024 + 46,
0115 .hsync_end = 1024 + 46 + 80,
0116 .htotal = 1024 + 46 + 80 + 100,
0117 .vdisplay = 600,
0118 .vsync_start = 600 + 5,
0119 .vsync_end = 600 + 5 + 5,
0120 .vtotal = 600 + 5 + 5 + 20,
0121 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
0122 };
0123
0124 static int tdo_tl070wsh30_panel_get_modes(struct drm_panel *panel,
0125 struct drm_connector *connector)
0126 {
0127 struct drm_display_mode *mode;
0128
0129 mode = drm_mode_duplicate(connector->dev, &default_mode);
0130 if (!mode) {
0131 dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
0132 default_mode.hdisplay, default_mode.vdisplay,
0133 drm_mode_vrefresh(&default_mode));
0134 return -ENOMEM;
0135 }
0136
0137 drm_mode_set_name(mode);
0138
0139 drm_mode_probed_add(connector, mode);
0140
0141 connector->display_info.width_mm = 154;
0142 connector->display_info.height_mm = 85;
0143 connector->display_info.bpc = 8;
0144
0145 return 1;
0146 }
0147
0148 static const struct drm_panel_funcs tdo_tl070wsh30_panel_funcs = {
0149 .unprepare = tdo_tl070wsh30_panel_unprepare,
0150 .prepare = tdo_tl070wsh30_panel_prepare,
0151 .get_modes = tdo_tl070wsh30_panel_get_modes,
0152 };
0153
0154 static const struct of_device_id tdo_tl070wsh30_of_match[] = {
0155 { .compatible = "tdo,tl070wsh30", },
0156 { }
0157 };
0158 MODULE_DEVICE_TABLE(of, tdo_tl070wsh30_of_match);
0159
0160 static int tdo_tl070wsh30_panel_add(struct tdo_tl070wsh30_panel *tdo_tl070wsh30)
0161 {
0162 struct device *dev = &tdo_tl070wsh30->link->dev;
0163 int err;
0164
0165 tdo_tl070wsh30->supply = devm_regulator_get(dev, "power");
0166 if (IS_ERR(tdo_tl070wsh30->supply))
0167 return PTR_ERR(tdo_tl070wsh30->supply);
0168
0169 tdo_tl070wsh30->reset_gpio = devm_gpiod_get(dev, "reset",
0170 GPIOD_OUT_LOW);
0171 if (IS_ERR(tdo_tl070wsh30->reset_gpio)) {
0172 err = PTR_ERR(tdo_tl070wsh30->reset_gpio);
0173 dev_dbg(dev, "failed to get reset gpio: %d\n", err);
0174 return err;
0175 }
0176
0177 drm_panel_init(&tdo_tl070wsh30->base, &tdo_tl070wsh30->link->dev,
0178 &tdo_tl070wsh30_panel_funcs, DRM_MODE_CONNECTOR_DSI);
0179
0180 err = drm_panel_of_backlight(&tdo_tl070wsh30->base);
0181 if (err)
0182 return err;
0183
0184 drm_panel_add(&tdo_tl070wsh30->base);
0185
0186 return 0;
0187 }
0188
0189 static int tdo_tl070wsh30_panel_probe(struct mipi_dsi_device *dsi)
0190 {
0191 struct tdo_tl070wsh30_panel *tdo_tl070wsh30;
0192 int err;
0193
0194 dsi->lanes = 4;
0195 dsi->format = MIPI_DSI_FMT_RGB888;
0196 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
0197
0198 tdo_tl070wsh30 = devm_kzalloc(&dsi->dev, sizeof(*tdo_tl070wsh30),
0199 GFP_KERNEL);
0200 if (!tdo_tl070wsh30)
0201 return -ENOMEM;
0202
0203 mipi_dsi_set_drvdata(dsi, tdo_tl070wsh30);
0204 tdo_tl070wsh30->link = dsi;
0205
0206 err = tdo_tl070wsh30_panel_add(tdo_tl070wsh30);
0207 if (err < 0)
0208 return err;
0209
0210 return mipi_dsi_attach(dsi);
0211 }
0212
0213 static int tdo_tl070wsh30_panel_remove(struct mipi_dsi_device *dsi)
0214 {
0215 struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi);
0216 int err;
0217
0218 err = mipi_dsi_detach(dsi);
0219 if (err < 0)
0220 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
0221
0222 drm_panel_remove(&tdo_tl070wsh30->base);
0223 drm_panel_disable(&tdo_tl070wsh30->base);
0224 drm_panel_unprepare(&tdo_tl070wsh30->base);
0225
0226 return 0;
0227 }
0228
0229 static void tdo_tl070wsh30_panel_shutdown(struct mipi_dsi_device *dsi)
0230 {
0231 struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi);
0232
0233 drm_panel_disable(&tdo_tl070wsh30->base);
0234 drm_panel_unprepare(&tdo_tl070wsh30->base);
0235 }
0236
0237 static struct mipi_dsi_driver tdo_tl070wsh30_panel_driver = {
0238 .driver = {
0239 .name = "panel-tdo-tl070wsh30",
0240 .of_match_table = tdo_tl070wsh30_of_match,
0241 },
0242 .probe = tdo_tl070wsh30_panel_probe,
0243 .remove = tdo_tl070wsh30_panel_remove,
0244 .shutdown = tdo_tl070wsh30_panel_shutdown,
0245 };
0246 module_mipi_dsi_driver(tdo_tl070wsh30_panel_driver);
0247
0248 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
0249 MODULE_DESCRIPTION("TDO TL070WSH30 panel driver");
0250 MODULE_LICENSE("GPL v2");