1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <linux/array_size.h> 4 #include <linux/delay.h> 5 #include <linux/err.h> 6 #include <linux/gpio/consumer.h> 7 #include <linux/mod_devicetable.h> 8 #include <linux/module.h> 9 #include <linux/property.h> 10 #include <linux/regulator/consumer.h> 11 12 #include <video/mipi_display.h> 13 14 #include <drm/drm_mipi_dsi.h> 15 #include <drm/drm_modes.h> 16 #include <drm/drm_panel.h> 17 #include <drm/drm_probe_helper.h> 18 19 #define R61307_MACP 0xb0 /* Manufacturer CMD Protect */ 20 #define R61307_MACP_ON 0x03 21 #define R61307_MACP_OFF 0x04 22 23 #define R61307_INVERSION 0xc1 24 #define R61307_GAMMA_SET_A 0xc8 /* Gamma Setting A */ 25 #define R61307_GAMMA_SET_B 0xc9 /* Gamma Setting B */ 26 #define R61307_GAMMA_SET_C 0xca /* Gamma Setting C */ 27 #define R61307_CONTRAST_SET 0xcc 28 29 struct renesas_r61307 { 30 struct drm_panel panel; 31 struct mipi_dsi_device *dsi; 32 33 struct regulator *vcc_supply; 34 struct regulator *iovcc_supply; 35 36 struct gpio_desc *reset_gpio; 37 38 bool dig_cont_adj; 39 bool inversion; 40 u32 gamma; 41 }; 42 43 static const u8 gamma_setting[][25] = { 44 { /* sentinel */ }, 45 { 46 R61307_GAMMA_SET_A, 47 0x00, 0x06, 0x0a, 0x0f, 48 0x14, 0x1f, 0x1f, 0x17, 49 0x12, 0x0c, 0x09, 0x06, 50 0x00, 0x06, 0x0a, 0x0f, 51 0x14, 0x1f, 0x1f, 0x17, 52 0x12, 0x0c, 0x09, 0x06 53 }, 54 { 55 R61307_GAMMA_SET_A, 56 0x00, 0x05, 0x0b, 0x0f, 57 0x11, 0x1d, 0x20, 0x18, 58 0x18, 0x09, 0x07, 0x06, 59 0x00, 0x05, 0x0b, 0x0f, 60 0x11, 0x1d, 0x20, 0x18, 61 0x18, 0x09, 0x07, 0x06 62 }, 63 { 64 R61307_GAMMA_SET_A, 65 0x0b, 0x0d, 0x10, 0x14, 66 0x13, 0x1d, 0x20, 0x18, 67 0x12, 0x09, 0x07, 0x06, 68 0x0a, 0x0c, 0x10, 0x14, 69 0x13, 0x1d, 0x20, 0x18, 70 0x12, 0x09, 0x07, 0x06 71 }, 72 }; 73 74 static inline struct renesas_r61307 *to_renesas_r61307(struct drm_panel *panel) 75 { 76 return container_of(panel, struct renesas_r61307, panel); 77 } 78 79 static void renesas_r61307_reset(struct renesas_r61307 *priv) 80 { 81 gpiod_set_value_cansleep(priv->reset_gpio, 1); 82 usleep_range(10000, 11000); 83 gpiod_set_value_cansleep(priv->reset_gpio, 0); 84 usleep_range(2000, 3000); 85 } 86 87 static int renesas_r61307_prepare(struct drm_panel *panel) 88 { 89 struct renesas_r61307 *priv = to_renesas_r61307(panel); 90 struct device *dev = &priv->dsi->dev; 91 int ret; 92 93 ret = regulator_enable(priv->vcc_supply); 94 if (ret) { 95 dev_err(dev, "failed to enable vcc power supply\n"); 96 return ret; 97 } 98 99 usleep_range(2000, 3000); 100 101 ret = regulator_enable(priv->iovcc_supply); 102 if (ret) { 103 dev_err(dev, "failed to enable iovcc power supply\n"); 104 return ret; 105 } 106 107 usleep_range(2000, 3000); 108 109 renesas_r61307_reset(priv); 110 111 return 0; 112 } 113 114 static int renesas_r61307_enable(struct drm_panel *panel) 115 { 116 struct renesas_r61307 *priv = to_renesas_r61307(panel); 117 struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; 118 119 mipi_dsi_dcs_exit_sleep_mode_multi(&ctx); 120 mipi_dsi_msleep(&ctx, 80); 121 122 mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); 123 mipi_dsi_msleep(&ctx, 20); 124 125 mipi_dsi_dcs_set_pixel_format_multi(&ctx, MIPI_DCS_PIXEL_FMT_24BIT << 4); 126 127 /* MACP Off */ 128 mipi_dsi_generic_write_seq_multi(&ctx, R61307_MACP, R61307_MACP_OFF); 129 130 if (priv->dig_cont_adj) 131 mipi_dsi_generic_write_seq_multi(&ctx, R61307_CONTRAST_SET, 132 0xdc, 0xb4, 0xff); 133 134 if (priv->gamma) 135 mipi_dsi_generic_write_multi(&ctx, gamma_setting[priv->gamma], 136 sizeof(gamma_setting[priv->gamma])); 137 138 if (priv->inversion) 139 mipi_dsi_generic_write_seq_multi(&ctx, R61307_INVERSION, 140 0x00, 0x50, 0x03, 0x22, 141 0x16, 0x06, 0x60, 0x11); 142 else 143 mipi_dsi_generic_write_seq_multi(&ctx, R61307_INVERSION, 144 0x00, 0x10, 0x03, 0x22, 145 0x16, 0x06, 0x60, 0x01); 146 147 /* MACP On */ 148 mipi_dsi_generic_write_seq_multi(&ctx, R61307_MACP, R61307_MACP_ON); 149 150 mipi_dsi_dcs_set_display_on_multi(&ctx); 151 mipi_dsi_msleep(&ctx, 50); 152 153 return ctx.accum_err; 154 } 155 156 static int renesas_r61307_disable(struct drm_panel *panel) 157 { 158 struct renesas_r61307 *priv = to_renesas_r61307(panel); 159 struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; 160 161 mipi_dsi_dcs_set_display_off_multi(&ctx); 162 mipi_dsi_msleep(&ctx, 100); 163 mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); 164 165 return ctx.accum_err; 166 } 167 168 static int renesas_r61307_unprepare(struct drm_panel *panel) 169 { 170 struct renesas_r61307 *priv = to_renesas_r61307(panel); 171 172 usleep_range(10000, 11000); 173 174 gpiod_set_value_cansleep(priv->reset_gpio, 1); 175 usleep_range(5000, 6000); 176 177 regulator_disable(priv->iovcc_supply); 178 usleep_range(2000, 3000); 179 regulator_disable(priv->vcc_supply); 180 181 return 0; 182 } 183 184 static const struct drm_display_mode renesas_r61307_mode = { 185 .clock = (768 + 116 + 81 + 5) * (1024 + 24 + 8 + 2) * 60 / 1000, 186 .hdisplay = 768, 187 .hsync_start = 768 + 116, 188 .hsync_end = 768 + 116 + 81, 189 .htotal = 768 + 116 + 81 + 5, 190 .vdisplay = 1024, 191 .vsync_start = 1024 + 24, 192 .vsync_end = 1024 + 24 + 8, 193 .vtotal = 1024 + 24 + 8 + 2, 194 .width_mm = 76, 195 .height_mm = 101, 196 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 197 }; 198 199 static int renesas_r61307_get_modes(struct drm_panel *panel, 200 struct drm_connector *connector) 201 { 202 return drm_connector_helper_get_modes_fixed(connector, &renesas_r61307_mode); 203 } 204 205 static const struct drm_panel_funcs renesas_r61307_panel_funcs = { 206 .prepare = renesas_r61307_prepare, 207 .enable = renesas_r61307_enable, 208 .disable = renesas_r61307_disable, 209 .unprepare = renesas_r61307_unprepare, 210 .get_modes = renesas_r61307_get_modes, 211 }; 212 213 static int renesas_r61307_probe(struct mipi_dsi_device *dsi) 214 { 215 struct device *dev = &dsi->dev; 216 struct renesas_r61307 *priv; 217 int ret; 218 219 priv = devm_drm_panel_alloc(dev, struct renesas_r61307, panel, 220 &renesas_r61307_panel_funcs, 221 DRM_MODE_CONNECTOR_DSI); 222 if (IS_ERR(priv)) 223 return PTR_ERR(priv); 224 225 priv->vcc_supply = devm_regulator_get(dev, "vcc"); 226 if (IS_ERR(priv->vcc_supply)) 227 return dev_err_probe(dev, PTR_ERR(priv->vcc_supply), 228 "Failed to get vcc-supply\n"); 229 230 priv->iovcc_supply = devm_regulator_get(dev, "iovcc"); 231 if (IS_ERR(priv->iovcc_supply)) 232 return dev_err_probe(dev, PTR_ERR(priv->iovcc_supply), 233 "Failed to get iovcc-supply\n"); 234 235 priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", 236 GPIOD_OUT_HIGH); 237 if (IS_ERR(priv->reset_gpio)) 238 return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), 239 "Failed to get reset gpios\n"); 240 241 if (device_property_read_bool(dev, "renesas,column-inversion")) 242 priv->inversion = true; 243 244 if (device_property_read_bool(dev, "renesas,contrast")) 245 priv->dig_cont_adj = true; 246 247 priv->gamma = 0; 248 device_property_read_u32(dev, "renesas,gamma", &priv->gamma); 249 250 priv->dsi = dsi; 251 mipi_dsi_set_drvdata(dsi, priv); 252 253 dsi->lanes = 4; 254 dsi->format = MIPI_DSI_FMT_RGB888; 255 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 256 MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; 257 258 ret = drm_panel_of_backlight(&priv->panel); 259 if (ret) 260 return dev_err_probe(dev, ret, "Failed to get backlight\n"); 261 262 drm_panel_add(&priv->panel); 263 264 ret = devm_mipi_dsi_attach(dev, dsi); 265 if (ret) { 266 drm_panel_remove(&priv->panel); 267 return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 268 } 269 270 return 0; 271 } 272 273 static void renesas_r61307_remove(struct mipi_dsi_device *dsi) 274 { 275 struct renesas_r61307 *priv = mipi_dsi_get_drvdata(dsi); 276 277 drm_panel_remove(&priv->panel); 278 } 279 280 static const struct of_device_id renesas_r61307_of_match[] = { 281 { .compatible = "hit,tx13d100vm0eaa" }, 282 { .compatible = "koe,tx13d100vm0eaa" }, 283 { /* sentinel */ } 284 }; 285 MODULE_DEVICE_TABLE(of, renesas_r61307_of_match); 286 287 static struct mipi_dsi_driver renesas_r61307_driver = { 288 .probe = renesas_r61307_probe, 289 .remove = renesas_r61307_remove, 290 .driver = { 291 .name = "panel-renesas-r61307", 292 .of_match_table = renesas_r61307_of_match, 293 }, 294 }; 295 module_mipi_dsi_driver(renesas_r61307_driver); 296 297 MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); 298 MODULE_DESCRIPTION("Renesas R61307-based panel driver"); 299 MODULE_LICENSE("GPL"); 300