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