panel-tpo-td028ttec1.c (12301B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Toppoly TD028TTEC1 panel support 4 * 5 * Copyright (C) 2008 Nokia Corporation 6 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> 7 * 8 * Neo 1973 code (jbt6k74.c): 9 * Copyright (C) 2006-2007 by OpenMoko, Inc. 10 * Author: Harald Welte <laforge@openmoko.org> 11 * 12 * Ported and adapted from Neo 1973 U-Boot by: 13 * H. Nikolaus Schaller <hns@goldelico.com> 14 */ 15 16#include <linux/module.h> 17#include <linux/delay.h> 18#include <linux/spi/spi.h> 19#include <linux/gpio.h> 20#include <video/omapfb_dss.h> 21 22struct panel_drv_data { 23 struct omap_dss_device dssdev; 24 struct omap_dss_device *in; 25 26 int data_lines; 27 28 struct omap_video_timings videomode; 29 30 struct spi_device *spi_dev; 31}; 32 33static const struct omap_video_timings td028ttec1_panel_timings = { 34 .x_res = 480, 35 .y_res = 640, 36 .pixelclock = 22153000, 37 .hfp = 24, 38 .hsw = 8, 39 .hbp = 8, 40 .vfp = 4, 41 .vsw = 2, 42 .vbp = 2, 43 44 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, 45 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, 46 47 .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, 48 .de_level = OMAPDSS_SIG_ACTIVE_HIGH, 49 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, 50}; 51 52#define JBT_COMMAND 0x000 53#define JBT_DATA 0x100 54 55static int jbt_ret_write_0(struct panel_drv_data *ddata, u8 reg) 56{ 57 int rc; 58 u16 tx_buf = JBT_COMMAND | reg; 59 60 rc = spi_write(ddata->spi_dev, (u8 *)&tx_buf, 61 1*sizeof(u16)); 62 if (rc != 0) 63 dev_err(&ddata->spi_dev->dev, 64 "jbt_ret_write_0 spi_write ret %d\n", rc); 65 66 return rc; 67} 68 69static int jbt_reg_write_1(struct panel_drv_data *ddata, u8 reg, u8 data) 70{ 71 int rc; 72 u16 tx_buf[2]; 73 74 tx_buf[0] = JBT_COMMAND | reg; 75 tx_buf[1] = JBT_DATA | data; 76 rc = spi_write(ddata->spi_dev, (u8 *)tx_buf, 77 2*sizeof(u16)); 78 if (rc != 0) 79 dev_err(&ddata->spi_dev->dev, 80 "jbt_reg_write_1 spi_write ret %d\n", rc); 81 82 return rc; 83} 84 85static int jbt_reg_write_2(struct panel_drv_data *ddata, u8 reg, u16 data) 86{ 87 int rc; 88 u16 tx_buf[3]; 89 90 tx_buf[0] = JBT_COMMAND | reg; 91 tx_buf[1] = JBT_DATA | (data >> 8); 92 tx_buf[2] = JBT_DATA | (data & 0xff); 93 94 rc = spi_write(ddata->spi_dev, (u8 *)tx_buf, 95 3*sizeof(u16)); 96 97 if (rc != 0) 98 dev_err(&ddata->spi_dev->dev, 99 "jbt_reg_write_2 spi_write ret %d\n", rc); 100 101 return rc; 102} 103 104enum jbt_register { 105 JBT_REG_SLEEP_IN = 0x10, 106 JBT_REG_SLEEP_OUT = 0x11, 107 108 JBT_REG_DISPLAY_OFF = 0x28, 109 JBT_REG_DISPLAY_ON = 0x29, 110 111 JBT_REG_RGB_FORMAT = 0x3a, 112 JBT_REG_QUAD_RATE = 0x3b, 113 114 JBT_REG_POWER_ON_OFF = 0xb0, 115 JBT_REG_BOOSTER_OP = 0xb1, 116 JBT_REG_BOOSTER_MODE = 0xb2, 117 JBT_REG_BOOSTER_FREQ = 0xb3, 118 JBT_REG_OPAMP_SYSCLK = 0xb4, 119 JBT_REG_VSC_VOLTAGE = 0xb5, 120 JBT_REG_VCOM_VOLTAGE = 0xb6, 121 JBT_REG_EXT_DISPL = 0xb7, 122 JBT_REG_OUTPUT_CONTROL = 0xb8, 123 JBT_REG_DCCLK_DCEV = 0xb9, 124 JBT_REG_DISPLAY_MODE1 = 0xba, 125 JBT_REG_DISPLAY_MODE2 = 0xbb, 126 JBT_REG_DISPLAY_MODE = 0xbc, 127 JBT_REG_ASW_SLEW = 0xbd, 128 JBT_REG_DUMMY_DISPLAY = 0xbe, 129 JBT_REG_DRIVE_SYSTEM = 0xbf, 130 131 JBT_REG_SLEEP_OUT_FR_A = 0xc0, 132 JBT_REG_SLEEP_OUT_FR_B = 0xc1, 133 JBT_REG_SLEEP_OUT_FR_C = 0xc2, 134 JBT_REG_SLEEP_IN_LCCNT_D = 0xc3, 135 JBT_REG_SLEEP_IN_LCCNT_E = 0xc4, 136 JBT_REG_SLEEP_IN_LCCNT_F = 0xc5, 137 JBT_REG_SLEEP_IN_LCCNT_G = 0xc6, 138 139 JBT_REG_GAMMA1_FINE_1 = 0xc7, 140 JBT_REG_GAMMA1_FINE_2 = 0xc8, 141 JBT_REG_GAMMA1_INCLINATION = 0xc9, 142 JBT_REG_GAMMA1_BLUE_OFFSET = 0xca, 143 144 JBT_REG_BLANK_CONTROL = 0xcf, 145 JBT_REG_BLANK_TH_TV = 0xd0, 146 JBT_REG_CKV_ON_OFF = 0xd1, 147 JBT_REG_CKV_1_2 = 0xd2, 148 JBT_REG_OEV_TIMING = 0xd3, 149 JBT_REG_ASW_TIMING_1 = 0xd4, 150 JBT_REG_ASW_TIMING_2 = 0xd5, 151 152 JBT_REG_HCLOCK_VGA = 0xec, 153 JBT_REG_HCLOCK_QVGA = 0xed, 154}; 155 156#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) 157 158static int td028ttec1_panel_connect(struct omap_dss_device *dssdev) 159{ 160 struct panel_drv_data *ddata = to_panel_data(dssdev); 161 struct omap_dss_device *in = ddata->in; 162 int r; 163 164 if (omapdss_device_is_connected(dssdev)) 165 return 0; 166 167 r = in->ops.dpi->connect(in, dssdev); 168 if (r) 169 return r; 170 171 return 0; 172} 173 174static void td028ttec1_panel_disconnect(struct omap_dss_device *dssdev) 175{ 176 struct panel_drv_data *ddata = to_panel_data(dssdev); 177 struct omap_dss_device *in = ddata->in; 178 179 if (!omapdss_device_is_connected(dssdev)) 180 return; 181 182 in->ops.dpi->disconnect(in, dssdev); 183} 184 185static int td028ttec1_panel_enable(struct omap_dss_device *dssdev) 186{ 187 struct panel_drv_data *ddata = to_panel_data(dssdev); 188 struct omap_dss_device *in = ddata->in; 189 int r; 190 191 if (!omapdss_device_is_connected(dssdev)) 192 return -ENODEV; 193 194 if (omapdss_device_is_enabled(dssdev)) 195 return 0; 196 197 if (ddata->data_lines) 198 in->ops.dpi->set_data_lines(in, ddata->data_lines); 199 in->ops.dpi->set_timings(in, &ddata->videomode); 200 201 r = in->ops.dpi->enable(in); 202 if (r) 203 return r; 204 205 dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n", 206 dssdev->state); 207 208 /* three times command zero */ 209 r |= jbt_ret_write_0(ddata, 0x00); 210 usleep_range(1000, 2000); 211 r |= jbt_ret_write_0(ddata, 0x00); 212 usleep_range(1000, 2000); 213 r |= jbt_ret_write_0(ddata, 0x00); 214 usleep_range(1000, 2000); 215 216 if (r) { 217 dev_warn(dssdev->dev, "transfer error\n"); 218 goto transfer_err; 219 } 220 221 /* deep standby out */ 222 r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x17); 223 224 /* RGB I/F on, RAM write off, QVGA through, SIGCON enable */ 225 r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE, 0x80); 226 227 /* Quad mode off */ 228 r |= jbt_reg_write_1(ddata, JBT_REG_QUAD_RATE, 0x00); 229 230 /* AVDD on, XVDD on */ 231 r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x16); 232 233 /* Output control */ 234 r |= jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0xfff9); 235 236 /* Sleep mode off */ 237 r |= jbt_ret_write_0(ddata, JBT_REG_SLEEP_OUT); 238 239 /* at this point we have like 50% grey */ 240 241 /* initialize register set */ 242 r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE1, 0x01); 243 r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE2, 0x00); 244 r |= jbt_reg_write_1(ddata, JBT_REG_RGB_FORMAT, 0x60); 245 r |= jbt_reg_write_1(ddata, JBT_REG_DRIVE_SYSTEM, 0x10); 246 r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_OP, 0x56); 247 r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_MODE, 0x33); 248 r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11); 249 r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11); 250 r |= jbt_reg_write_1(ddata, JBT_REG_OPAMP_SYSCLK, 0x02); 251 r |= jbt_reg_write_1(ddata, JBT_REG_VSC_VOLTAGE, 0x2b); 252 r |= jbt_reg_write_1(ddata, JBT_REG_VCOM_VOLTAGE, 0x40); 253 r |= jbt_reg_write_1(ddata, JBT_REG_EXT_DISPL, 0x03); 254 r |= jbt_reg_write_1(ddata, JBT_REG_DCCLK_DCEV, 0x04); 255 /* 256 * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement 257 * to avoid red / blue flicker 258 */ 259 r |= jbt_reg_write_1(ddata, JBT_REG_ASW_SLEW, 0x04); 260 r |= jbt_reg_write_1(ddata, JBT_REG_DUMMY_DISPLAY, 0x00); 261 262 r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_A, 0x11); 263 r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_B, 0x11); 264 r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_C, 0x11); 265 r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040); 266 r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0); 267 r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020); 268 r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0); 269 270 r |= jbt_reg_write_2(ddata, JBT_REG_GAMMA1_FINE_1, 0x5533); 271 r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_FINE_2, 0x00); 272 r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_INCLINATION, 0x00); 273 r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00); 274 275 r |= jbt_reg_write_2(ddata, JBT_REG_HCLOCK_VGA, 0x1f0); 276 r |= jbt_reg_write_1(ddata, JBT_REG_BLANK_CONTROL, 0x02); 277 r |= jbt_reg_write_2(ddata, JBT_REG_BLANK_TH_TV, 0x0804); 278 279 r |= jbt_reg_write_1(ddata, JBT_REG_CKV_ON_OFF, 0x01); 280 r |= jbt_reg_write_2(ddata, JBT_REG_CKV_1_2, 0x0000); 281 282 r |= jbt_reg_write_2(ddata, JBT_REG_OEV_TIMING, 0x0d0e); 283 r |= jbt_reg_write_2(ddata, JBT_REG_ASW_TIMING_1, 0x11a4); 284 r |= jbt_reg_write_1(ddata, JBT_REG_ASW_TIMING_2, 0x0e); 285 286 r |= jbt_ret_write_0(ddata, JBT_REG_DISPLAY_ON); 287 288 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 289 290transfer_err: 291 292 return r ? -EIO : 0; 293} 294 295static void td028ttec1_panel_disable(struct omap_dss_device *dssdev) 296{ 297 struct panel_drv_data *ddata = to_panel_data(dssdev); 298 struct omap_dss_device *in = ddata->in; 299 300 if (!omapdss_device_is_enabled(dssdev)) 301 return; 302 303 dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n"); 304 305 jbt_ret_write_0(ddata, JBT_REG_DISPLAY_OFF); 306 jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002); 307 jbt_ret_write_0(ddata, JBT_REG_SLEEP_IN); 308 jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x00); 309 310 in->ops.dpi->disable(in); 311 312 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 313} 314 315static void td028ttec1_panel_set_timings(struct omap_dss_device *dssdev, 316 struct omap_video_timings *timings) 317{ 318 struct panel_drv_data *ddata = to_panel_data(dssdev); 319 struct omap_dss_device *in = ddata->in; 320 321 ddata->videomode = *timings; 322 dssdev->panel.timings = *timings; 323 324 in->ops.dpi->set_timings(in, timings); 325} 326 327static void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev, 328 struct omap_video_timings *timings) 329{ 330 struct panel_drv_data *ddata = to_panel_data(dssdev); 331 332 *timings = ddata->videomode; 333} 334 335static int td028ttec1_panel_check_timings(struct omap_dss_device *dssdev, 336 struct omap_video_timings *timings) 337{ 338 struct panel_drv_data *ddata = to_panel_data(dssdev); 339 struct omap_dss_device *in = ddata->in; 340 341 return in->ops.dpi->check_timings(in, timings); 342} 343 344static struct omap_dss_driver td028ttec1_ops = { 345 .connect = td028ttec1_panel_connect, 346 .disconnect = td028ttec1_panel_disconnect, 347 348 .enable = td028ttec1_panel_enable, 349 .disable = td028ttec1_panel_disable, 350 351 .set_timings = td028ttec1_panel_set_timings, 352 .get_timings = td028ttec1_panel_get_timings, 353 .check_timings = td028ttec1_panel_check_timings, 354}; 355 356static int td028ttec1_probe_of(struct spi_device *spi) 357{ 358 struct device_node *node = spi->dev.of_node; 359 struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); 360 struct omap_dss_device *in; 361 362 in = omapdss_of_find_source_for_first_ep(node); 363 if (IS_ERR(in)) { 364 dev_err(&spi->dev, "failed to find video source\n"); 365 return PTR_ERR(in); 366 } 367 368 ddata->in = in; 369 370 return 0; 371} 372 373static int td028ttec1_panel_probe(struct spi_device *spi) 374{ 375 struct panel_drv_data *ddata; 376 struct omap_dss_device *dssdev; 377 int r; 378 379 dev_dbg(&spi->dev, "%s\n", __func__); 380 381 if (!spi->dev.of_node) 382 return -ENODEV; 383 384 spi->bits_per_word = 9; 385 spi->mode = SPI_MODE_3; 386 387 r = spi_setup(spi); 388 if (r < 0) { 389 dev_err(&spi->dev, "spi_setup failed: %d\n", r); 390 return r; 391 } 392 393 ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); 394 if (ddata == NULL) 395 return -ENOMEM; 396 397 dev_set_drvdata(&spi->dev, ddata); 398 399 ddata->spi_dev = spi; 400 401 r = td028ttec1_probe_of(spi); 402 if (r) 403 return r; 404 405 ddata->videomode = td028ttec1_panel_timings; 406 407 dssdev = &ddata->dssdev; 408 dssdev->dev = &spi->dev; 409 dssdev->driver = &td028ttec1_ops; 410 dssdev->type = OMAP_DISPLAY_TYPE_DPI; 411 dssdev->owner = THIS_MODULE; 412 dssdev->panel.timings = ddata->videomode; 413 dssdev->phy.dpi.data_lines = ddata->data_lines; 414 415 r = omapdss_register_display(dssdev); 416 if (r) { 417 dev_err(&spi->dev, "Failed to register panel\n"); 418 goto err_reg; 419 } 420 421 return 0; 422 423err_reg: 424 omap_dss_put_device(ddata->in); 425 return r; 426} 427 428static void td028ttec1_panel_remove(struct spi_device *spi) 429{ 430 struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); 431 struct omap_dss_device *dssdev = &ddata->dssdev; 432 struct omap_dss_device *in = ddata->in; 433 434 dev_dbg(&ddata->spi_dev->dev, "%s\n", __func__); 435 436 omapdss_unregister_display(dssdev); 437 438 td028ttec1_panel_disable(dssdev); 439 td028ttec1_panel_disconnect(dssdev); 440 441 omap_dss_put_device(in); 442} 443 444static const struct of_device_id td028ttec1_of_match[] = { 445 { .compatible = "omapdss,tpo,td028ttec1", }, 446 /* keep to not break older DTB */ 447 { .compatible = "omapdss,toppoly,td028ttec1", }, 448 {}, 449}; 450 451MODULE_DEVICE_TABLE(of, td028ttec1_of_match); 452 453static const struct spi_device_id td028ttec1_ids[] = { 454 { "toppoly,td028ttec1", 0 }, 455 { "tpo,td028ttec1", 0}, 456 { /* sentinel */ } 457}; 458 459MODULE_DEVICE_TABLE(spi, td028ttec1_ids); 460 461static struct spi_driver td028ttec1_spi_driver = { 462 .probe = td028ttec1_panel_probe, 463 .remove = td028ttec1_panel_remove, 464 .id_table = td028ttec1_ids, 465 466 .driver = { 467 .name = "panel-tpo-td028ttec1", 468 .of_match_table = td028ttec1_of_match, 469 .suppress_bind_attrs = true, 470 }, 471}; 472 473module_spi_driver(td028ttec1_spi_driver); 474 475MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>"); 476MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver"); 477MODULE_LICENSE("GPL");