1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2022 Konrad Dybcio <konrad.dybcio@somainline.org> 4 * 5 * Generated with linux-mdss-dsi-panel-driver-generator with a 6 * substantial amount of manual adjustments. 7 * 8 * SONY Downstream kernel calls this one: 9 * - "JDI ID3" for Akari (XZ2) 10 * - "JDI ID4" for Apollo (XZ2 Compact) 11 */ 12 13 #include <linux/delay.h> 14 #include <linux/gpio/consumer.h> 15 #include <linux/module.h> 16 #include <linux/of.h> 17 #include <linux/regulator/consumer.h> 18 19 #include <video/mipi_display.h> 20 21 #include <drm/drm_mipi_dsi.h> 22 #include <drm/drm_modes.h> 23 #include <drm/drm_panel.h> 24 25 enum { 26 TYPE_TAMA_60HZ, 27 /* 28 * Leaving room for expansion - SONY very often uses 29 * *truly reliably* overclockable panels on their flagships! 30 */ 31 }; 32 33 struct sony_td4353_jdi { 34 struct drm_panel panel; 35 struct mipi_dsi_device *dsi; 36 struct regulator_bulk_data supplies[3]; 37 struct gpio_desc *panel_reset_gpio; 38 struct gpio_desc *touch_reset_gpio; 39 int type; 40 }; 41 42 static inline struct sony_td4353_jdi *to_sony_td4353_jdi(struct drm_panel *panel) 43 { 44 return container_of(panel, struct sony_td4353_jdi, panel); 45 } 46 47 static int sony_td4353_jdi_on(struct sony_td4353_jdi *ctx) 48 { 49 struct mipi_dsi_device *dsi = ctx->dsi; 50 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 51 52 dsi->mode_flags |= MIPI_DSI_MODE_LPM; 53 54 mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0x0000, 1080 - 1); 55 mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0x0000, 2160 - 1); 56 mipi_dsi_dcs_set_tear_scanline_multi(&dsi_ctx, 0); 57 mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 58 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); 59 60 mipi_dsi_dcs_set_pixel_format_multi(&dsi_ctx, 0x77); 61 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_PARTIAL_ROWS, 62 0x00, 0x00, 0x08, 0x6f); 63 64 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 65 mipi_dsi_msleep(&dsi_ctx, 70); 66 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_MEMORY_START); 67 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 68 69 return dsi_ctx.accum_err; 70 } 71 72 static void sony_td4353_jdi_off(struct sony_td4353_jdi *ctx) 73 { 74 struct mipi_dsi_device *dsi = ctx->dsi; 75 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 76 77 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 78 79 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 80 mipi_dsi_msleep(&dsi_ctx, 22); 81 mipi_dsi_dcs_set_tear_off_multi(&dsi_ctx); 82 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 83 mipi_dsi_msleep(&dsi_ctx, 80); 84 } 85 86 static void sony_td4353_assert_reset_gpios(struct sony_td4353_jdi *ctx, int mode) 87 { 88 gpiod_set_value_cansleep(ctx->touch_reset_gpio, mode); 89 gpiod_set_value_cansleep(ctx->panel_reset_gpio, mode); 90 usleep_range(5000, 5100); 91 } 92 93 static int sony_td4353_jdi_prepare(struct drm_panel *panel) 94 { 95 struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel); 96 int ret; 97 98 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 99 if (ret < 0) 100 return ret; 101 102 msleep(100); 103 104 sony_td4353_assert_reset_gpios(ctx, 1); 105 106 ret = sony_td4353_jdi_on(ctx); 107 if (ret < 0) { 108 sony_td4353_assert_reset_gpios(ctx, 0); 109 regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 110 return ret; 111 } 112 113 return 0; 114 } 115 116 static int sony_td4353_jdi_unprepare(struct drm_panel *panel) 117 { 118 struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel); 119 120 sony_td4353_jdi_off(ctx); 121 122 sony_td4353_assert_reset_gpios(ctx, 0); 123 regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 124 125 return 0; 126 } 127 128 static const struct drm_display_mode sony_td4353_jdi_mode_tama_60hz = { 129 .clock = (1080 + 4 + 8 + 8) * (2160 + 259 + 8 + 8) * 60 / 1000, 130 .hdisplay = 1080, 131 .hsync_start = 1080 + 4, 132 .hsync_end = 1080 + 4 + 8, 133 .htotal = 1080 + 4 + 8 + 8, 134 .vdisplay = 2160, 135 .vsync_start = 2160 + 259, 136 .vsync_end = 2160 + 259 + 8, 137 .vtotal = 2160 + 259 + 8 + 8, 138 .width_mm = 64, 139 .height_mm = 128, 140 }; 141 142 static int sony_td4353_jdi_get_modes(struct drm_panel *panel, 143 struct drm_connector *connector) 144 { 145 struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel); 146 struct drm_display_mode *mode = NULL; 147 148 if (ctx->type == TYPE_TAMA_60HZ) 149 mode = drm_mode_duplicate(connector->dev, &sony_td4353_jdi_mode_tama_60hz); 150 else 151 return -EINVAL; 152 153 if (!mode) 154 return -ENOMEM; 155 156 drm_mode_set_name(mode); 157 158 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 159 connector->display_info.width_mm = mode->width_mm; 160 connector->display_info.height_mm = mode->height_mm; 161 drm_mode_probed_add(connector, mode); 162 163 return 1; 164 } 165 166 static const struct drm_panel_funcs sony_td4353_jdi_panel_funcs = { 167 .prepare = sony_td4353_jdi_prepare, 168 .unprepare = sony_td4353_jdi_unprepare, 169 .get_modes = sony_td4353_jdi_get_modes, 170 }; 171 172 static int sony_td4353_jdi_probe(struct mipi_dsi_device *dsi) 173 { 174 struct device *dev = &dsi->dev; 175 struct sony_td4353_jdi *ctx; 176 int ret; 177 178 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 179 if (!ctx) 180 return -ENOMEM; 181 182 ctx->type = (uintptr_t)of_device_get_match_data(dev); 183 184 ctx->supplies[0].supply = "vddio"; 185 ctx->supplies[1].supply = "vsp"; 186 ctx->supplies[2].supply = "vsn"; 187 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 188 ctx->supplies); 189 if (ret < 0) 190 return dev_err_probe(dev, ret, "Failed to get regulators\n"); 191 192 ctx->panel_reset_gpio = devm_gpiod_get(dev, "panel-reset", GPIOD_ASIS); 193 if (IS_ERR(ctx->panel_reset_gpio)) 194 return dev_err_probe(dev, PTR_ERR(ctx->panel_reset_gpio), 195 "Failed to get panel-reset-gpios\n"); 196 197 ctx->touch_reset_gpio = devm_gpiod_get(dev, "touch-reset", GPIOD_ASIS); 198 if (IS_ERR(ctx->touch_reset_gpio)) 199 return dev_err_probe(dev, PTR_ERR(ctx->touch_reset_gpio), 200 "Failed to get touch-reset-gpios\n"); 201 202 ctx->dsi = dsi; 203 mipi_dsi_set_drvdata(dsi, ctx); 204 205 dsi->lanes = 4; 206 dsi->format = MIPI_DSI_FMT_RGB888; 207 dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS; 208 209 drm_panel_init(&ctx->panel, dev, &sony_td4353_jdi_panel_funcs, 210 DRM_MODE_CONNECTOR_DSI); 211 212 ret = drm_panel_of_backlight(&ctx->panel); 213 if (ret) 214 return dev_err_probe(dev, ret, "Failed to get backlight\n"); 215 216 drm_panel_add(&ctx->panel); 217 218 ret = mipi_dsi_attach(dsi); 219 if (ret < 0) { 220 dev_err(dev, "Failed to attach to DSI host: %d\n", ret); 221 drm_panel_remove(&ctx->panel); 222 return ret; 223 } 224 225 return 0; 226 } 227 228 static void sony_td4353_jdi_remove(struct mipi_dsi_device *dsi) 229 { 230 struct sony_td4353_jdi *ctx = mipi_dsi_get_drvdata(dsi); 231 int ret; 232 233 ret = mipi_dsi_detach(dsi); 234 if (ret < 0) 235 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 236 237 drm_panel_remove(&ctx->panel); 238 } 239 240 static const struct of_device_id sony_td4353_jdi_of_match[] = { 241 { .compatible = "sony,td4353-jdi-tama", .data = (void *)TYPE_TAMA_60HZ }, 242 { /* sentinel */ } 243 }; 244 MODULE_DEVICE_TABLE(of, sony_td4353_jdi_of_match); 245 246 static struct mipi_dsi_driver sony_td4353_jdi_driver = { 247 .probe = sony_td4353_jdi_probe, 248 .remove = sony_td4353_jdi_remove, 249 .driver = { 250 .name = "panel-sony-td4353-jdi", 251 .of_match_table = sony_td4353_jdi_of_match, 252 }, 253 }; 254 module_mipi_dsi_driver(sony_td4353_jdi_driver); 255 256 MODULE_AUTHOR("Konrad Dybcio <konrad.dybcio@somainline.org>"); 257 MODULE_DESCRIPTION("DRM panel driver for SONY Xperia XZ2/XZ2c JDI panel"); 258 MODULE_LICENSE("GPL"); 259