162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Toppoly TD028TTEC1 panel support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 662306a36Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Neo 1973 code (jbt6k74.c): 962306a36Sopenharmony_ci * Copyright (C) 2006-2007 by OpenMoko, Inc. 1062306a36Sopenharmony_ci * Author: Harald Welte <laforge@openmoko.org> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Ported and adapted from Neo 1973 U-Boot by: 1362306a36Sopenharmony_ci * H. Nikolaus Schaller <hns@goldelico.com> 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/spi/spi.h> 1962306a36Sopenharmony_ci#include <video/omapfb_dss.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct panel_drv_data { 2262306a36Sopenharmony_ci struct omap_dss_device dssdev; 2362306a36Sopenharmony_ci struct omap_dss_device *in; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci int data_lines; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci struct omap_video_timings videomode; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci struct spi_device *spi_dev; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic const struct omap_video_timings td028ttec1_panel_timings = { 3362306a36Sopenharmony_ci .x_res = 480, 3462306a36Sopenharmony_ci .y_res = 640, 3562306a36Sopenharmony_ci .pixelclock = 22153000, 3662306a36Sopenharmony_ci .hfp = 24, 3762306a36Sopenharmony_ci .hsw = 8, 3862306a36Sopenharmony_ci .hbp = 8, 3962306a36Sopenharmony_ci .vfp = 4, 4062306a36Sopenharmony_ci .vsw = 2, 4162306a36Sopenharmony_ci .vbp = 2, 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, 4462306a36Sopenharmony_ci .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, 4762306a36Sopenharmony_ci .de_level = OMAPDSS_SIG_ACTIVE_HIGH, 4862306a36Sopenharmony_ci .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define JBT_COMMAND 0x000 5262306a36Sopenharmony_ci#define JBT_DATA 0x100 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic int jbt_ret_write_0(struct panel_drv_data *ddata, u8 reg) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int rc; 5762306a36Sopenharmony_ci u16 tx_buf = JBT_COMMAND | reg; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci rc = spi_write(ddata->spi_dev, (u8 *)&tx_buf, 6062306a36Sopenharmony_ci 1*sizeof(u16)); 6162306a36Sopenharmony_ci if (rc != 0) 6262306a36Sopenharmony_ci dev_err(&ddata->spi_dev->dev, 6362306a36Sopenharmony_ci "jbt_ret_write_0 spi_write ret %d\n", rc); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return rc; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int jbt_reg_write_1(struct panel_drv_data *ddata, u8 reg, u8 data) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci int rc; 7162306a36Sopenharmony_ci u16 tx_buf[2]; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci tx_buf[0] = JBT_COMMAND | reg; 7462306a36Sopenharmony_ci tx_buf[1] = JBT_DATA | data; 7562306a36Sopenharmony_ci rc = spi_write(ddata->spi_dev, (u8 *)tx_buf, 7662306a36Sopenharmony_ci 2*sizeof(u16)); 7762306a36Sopenharmony_ci if (rc != 0) 7862306a36Sopenharmony_ci dev_err(&ddata->spi_dev->dev, 7962306a36Sopenharmony_ci "jbt_reg_write_1 spi_write ret %d\n", rc); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return rc; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int jbt_reg_write_2(struct panel_drv_data *ddata, u8 reg, u16 data) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int rc; 8762306a36Sopenharmony_ci u16 tx_buf[3]; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci tx_buf[0] = JBT_COMMAND | reg; 9062306a36Sopenharmony_ci tx_buf[1] = JBT_DATA | (data >> 8); 9162306a36Sopenharmony_ci tx_buf[2] = JBT_DATA | (data & 0xff); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci rc = spi_write(ddata->spi_dev, (u8 *)tx_buf, 9462306a36Sopenharmony_ci 3*sizeof(u16)); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (rc != 0) 9762306a36Sopenharmony_ci dev_err(&ddata->spi_dev->dev, 9862306a36Sopenharmony_ci "jbt_reg_write_2 spi_write ret %d\n", rc); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return rc; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cienum jbt_register { 10462306a36Sopenharmony_ci JBT_REG_SLEEP_IN = 0x10, 10562306a36Sopenharmony_ci JBT_REG_SLEEP_OUT = 0x11, 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci JBT_REG_DISPLAY_OFF = 0x28, 10862306a36Sopenharmony_ci JBT_REG_DISPLAY_ON = 0x29, 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci JBT_REG_RGB_FORMAT = 0x3a, 11162306a36Sopenharmony_ci JBT_REG_QUAD_RATE = 0x3b, 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci JBT_REG_POWER_ON_OFF = 0xb0, 11462306a36Sopenharmony_ci JBT_REG_BOOSTER_OP = 0xb1, 11562306a36Sopenharmony_ci JBT_REG_BOOSTER_MODE = 0xb2, 11662306a36Sopenharmony_ci JBT_REG_BOOSTER_FREQ = 0xb3, 11762306a36Sopenharmony_ci JBT_REG_OPAMP_SYSCLK = 0xb4, 11862306a36Sopenharmony_ci JBT_REG_VSC_VOLTAGE = 0xb5, 11962306a36Sopenharmony_ci JBT_REG_VCOM_VOLTAGE = 0xb6, 12062306a36Sopenharmony_ci JBT_REG_EXT_DISPL = 0xb7, 12162306a36Sopenharmony_ci JBT_REG_OUTPUT_CONTROL = 0xb8, 12262306a36Sopenharmony_ci JBT_REG_DCCLK_DCEV = 0xb9, 12362306a36Sopenharmony_ci JBT_REG_DISPLAY_MODE1 = 0xba, 12462306a36Sopenharmony_ci JBT_REG_DISPLAY_MODE2 = 0xbb, 12562306a36Sopenharmony_ci JBT_REG_DISPLAY_MODE = 0xbc, 12662306a36Sopenharmony_ci JBT_REG_ASW_SLEW = 0xbd, 12762306a36Sopenharmony_ci JBT_REG_DUMMY_DISPLAY = 0xbe, 12862306a36Sopenharmony_ci JBT_REG_DRIVE_SYSTEM = 0xbf, 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci JBT_REG_SLEEP_OUT_FR_A = 0xc0, 13162306a36Sopenharmony_ci JBT_REG_SLEEP_OUT_FR_B = 0xc1, 13262306a36Sopenharmony_ci JBT_REG_SLEEP_OUT_FR_C = 0xc2, 13362306a36Sopenharmony_ci JBT_REG_SLEEP_IN_LCCNT_D = 0xc3, 13462306a36Sopenharmony_ci JBT_REG_SLEEP_IN_LCCNT_E = 0xc4, 13562306a36Sopenharmony_ci JBT_REG_SLEEP_IN_LCCNT_F = 0xc5, 13662306a36Sopenharmony_ci JBT_REG_SLEEP_IN_LCCNT_G = 0xc6, 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci JBT_REG_GAMMA1_FINE_1 = 0xc7, 13962306a36Sopenharmony_ci JBT_REG_GAMMA1_FINE_2 = 0xc8, 14062306a36Sopenharmony_ci JBT_REG_GAMMA1_INCLINATION = 0xc9, 14162306a36Sopenharmony_ci JBT_REG_GAMMA1_BLUE_OFFSET = 0xca, 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci JBT_REG_BLANK_CONTROL = 0xcf, 14462306a36Sopenharmony_ci JBT_REG_BLANK_TH_TV = 0xd0, 14562306a36Sopenharmony_ci JBT_REG_CKV_ON_OFF = 0xd1, 14662306a36Sopenharmony_ci JBT_REG_CKV_1_2 = 0xd2, 14762306a36Sopenharmony_ci JBT_REG_OEV_TIMING = 0xd3, 14862306a36Sopenharmony_ci JBT_REG_ASW_TIMING_1 = 0xd4, 14962306a36Sopenharmony_ci JBT_REG_ASW_TIMING_2 = 0xd5, 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci JBT_REG_HCLOCK_VGA = 0xec, 15262306a36Sopenharmony_ci JBT_REG_HCLOCK_QVGA = 0xed, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int td028ttec1_panel_connect(struct omap_dss_device *dssdev) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 16062306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 16162306a36Sopenharmony_ci int r; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (omapdss_device_is_connected(dssdev)) 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci r = in->ops.dpi->connect(in, dssdev); 16762306a36Sopenharmony_ci if (r) 16862306a36Sopenharmony_ci return r; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void td028ttec1_panel_disconnect(struct omap_dss_device *dssdev) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 17662306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!omapdss_device_is_connected(dssdev)) 17962306a36Sopenharmony_ci return; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci in->ops.dpi->disconnect(in, dssdev); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int td028ttec1_panel_enable(struct omap_dss_device *dssdev) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 18762306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 18862306a36Sopenharmony_ci int r; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (!omapdss_device_is_connected(dssdev)) 19162306a36Sopenharmony_ci return -ENODEV; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (omapdss_device_is_enabled(dssdev)) 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (ddata->data_lines) 19762306a36Sopenharmony_ci in->ops.dpi->set_data_lines(in, ddata->data_lines); 19862306a36Sopenharmony_ci in->ops.dpi->set_timings(in, &ddata->videomode); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci r = in->ops.dpi->enable(in); 20162306a36Sopenharmony_ci if (r) 20262306a36Sopenharmony_ci return r; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n", 20562306a36Sopenharmony_ci dssdev->state); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* three times command zero */ 20862306a36Sopenharmony_ci r |= jbt_ret_write_0(ddata, 0x00); 20962306a36Sopenharmony_ci usleep_range(1000, 2000); 21062306a36Sopenharmony_ci r |= jbt_ret_write_0(ddata, 0x00); 21162306a36Sopenharmony_ci usleep_range(1000, 2000); 21262306a36Sopenharmony_ci r |= jbt_ret_write_0(ddata, 0x00); 21362306a36Sopenharmony_ci usleep_range(1000, 2000); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (r) { 21662306a36Sopenharmony_ci dev_warn(dssdev->dev, "transfer error\n"); 21762306a36Sopenharmony_ci goto transfer_err; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* deep standby out */ 22162306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x17); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* RGB I/F on, RAM write off, QVGA through, SIGCON enable */ 22462306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE, 0x80); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* Quad mode off */ 22762306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_QUAD_RATE, 0x00); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* AVDD on, XVDD on */ 23062306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x16); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* Output control */ 23362306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0xfff9); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Sleep mode off */ 23662306a36Sopenharmony_ci r |= jbt_ret_write_0(ddata, JBT_REG_SLEEP_OUT); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* at this point we have like 50% grey */ 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* initialize register set */ 24162306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE1, 0x01); 24262306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE2, 0x00); 24362306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_RGB_FORMAT, 0x60); 24462306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_DRIVE_SYSTEM, 0x10); 24562306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_OP, 0x56); 24662306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_MODE, 0x33); 24762306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11); 24862306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11); 24962306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_OPAMP_SYSCLK, 0x02); 25062306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_VSC_VOLTAGE, 0x2b); 25162306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_VCOM_VOLTAGE, 0x40); 25262306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_EXT_DISPL, 0x03); 25362306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_DCCLK_DCEV, 0x04); 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement 25662306a36Sopenharmony_ci * to avoid red / blue flicker 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_ASW_SLEW, 0x04); 25962306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_DUMMY_DISPLAY, 0x00); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_A, 0x11); 26262306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_B, 0x11); 26362306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_C, 0x11); 26462306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040); 26562306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0); 26662306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020); 26762306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_GAMMA1_FINE_1, 0x5533); 27062306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_FINE_2, 0x00); 27162306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_INCLINATION, 0x00); 27262306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_HCLOCK_VGA, 0x1f0); 27562306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_BLANK_CONTROL, 0x02); 27662306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_BLANK_TH_TV, 0x0804); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_CKV_ON_OFF, 0x01); 27962306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_CKV_1_2, 0x0000); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_OEV_TIMING, 0x0d0e); 28262306a36Sopenharmony_ci r |= jbt_reg_write_2(ddata, JBT_REG_ASW_TIMING_1, 0x11a4); 28362306a36Sopenharmony_ci r |= jbt_reg_write_1(ddata, JBT_REG_ASW_TIMING_2, 0x0e); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci r |= jbt_ret_write_0(ddata, JBT_REG_DISPLAY_ON); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_citransfer_err: 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return r ? -EIO : 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void td028ttec1_panel_disable(struct omap_dss_device *dssdev) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 29762306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!omapdss_device_is_enabled(dssdev)) 30062306a36Sopenharmony_ci return; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n"); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci jbt_ret_write_0(ddata, JBT_REG_DISPLAY_OFF); 30562306a36Sopenharmony_ci jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002); 30662306a36Sopenharmony_ci jbt_ret_write_0(ddata, JBT_REG_SLEEP_IN); 30762306a36Sopenharmony_ci jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x00); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci in->ops.dpi->disable(in); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void td028ttec1_panel_set_timings(struct omap_dss_device *dssdev, 31562306a36Sopenharmony_ci struct omap_video_timings *timings) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 31862306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci ddata->videomode = *timings; 32162306a36Sopenharmony_ci dssdev->panel.timings = *timings; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci in->ops.dpi->set_timings(in, timings); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev, 32762306a36Sopenharmony_ci struct omap_video_timings *timings) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci *timings = ddata->videomode; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int td028ttec1_panel_check_timings(struct omap_dss_device *dssdev, 33562306a36Sopenharmony_ci struct omap_video_timings *timings) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 33862306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return in->ops.dpi->check_timings(in, timings); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic struct omap_dss_driver td028ttec1_ops = { 34462306a36Sopenharmony_ci .connect = td028ttec1_panel_connect, 34562306a36Sopenharmony_ci .disconnect = td028ttec1_panel_disconnect, 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci .enable = td028ttec1_panel_enable, 34862306a36Sopenharmony_ci .disable = td028ttec1_panel_disable, 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci .set_timings = td028ttec1_panel_set_timings, 35162306a36Sopenharmony_ci .get_timings = td028ttec1_panel_get_timings, 35262306a36Sopenharmony_ci .check_timings = td028ttec1_panel_check_timings, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int td028ttec1_probe_of(struct spi_device *spi) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct device_node *node = spi->dev.of_node; 35862306a36Sopenharmony_ci struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); 35962306a36Sopenharmony_ci struct omap_dss_device *in; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci in = omapdss_of_find_source_for_first_ep(node); 36262306a36Sopenharmony_ci if (IS_ERR(in)) { 36362306a36Sopenharmony_ci dev_err(&spi->dev, "failed to find video source\n"); 36462306a36Sopenharmony_ci return PTR_ERR(in); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ddata->in = in; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int td028ttec1_panel_probe(struct spi_device *spi) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct panel_drv_data *ddata; 37562306a36Sopenharmony_ci struct omap_dss_device *dssdev; 37662306a36Sopenharmony_ci int r; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci dev_dbg(&spi->dev, "%s\n", __func__); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (!spi->dev.of_node) 38162306a36Sopenharmony_ci return -ENODEV; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci spi->bits_per_word = 9; 38462306a36Sopenharmony_ci spi->mode = SPI_MODE_3; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci r = spi_setup(spi); 38762306a36Sopenharmony_ci if (r < 0) { 38862306a36Sopenharmony_ci dev_err(&spi->dev, "spi_setup failed: %d\n", r); 38962306a36Sopenharmony_ci return r; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); 39362306a36Sopenharmony_ci if (ddata == NULL) 39462306a36Sopenharmony_ci return -ENOMEM; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci dev_set_drvdata(&spi->dev, ddata); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci ddata->spi_dev = spi; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci r = td028ttec1_probe_of(spi); 40162306a36Sopenharmony_ci if (r) 40262306a36Sopenharmony_ci return r; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci ddata->videomode = td028ttec1_panel_timings; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci dssdev = &ddata->dssdev; 40762306a36Sopenharmony_ci dssdev->dev = &spi->dev; 40862306a36Sopenharmony_ci dssdev->driver = &td028ttec1_ops; 40962306a36Sopenharmony_ci dssdev->type = OMAP_DISPLAY_TYPE_DPI; 41062306a36Sopenharmony_ci dssdev->owner = THIS_MODULE; 41162306a36Sopenharmony_ci dssdev->panel.timings = ddata->videomode; 41262306a36Sopenharmony_ci dssdev->phy.dpi.data_lines = ddata->data_lines; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci r = omapdss_register_display(dssdev); 41562306a36Sopenharmony_ci if (r) { 41662306a36Sopenharmony_ci dev_err(&spi->dev, "Failed to register panel\n"); 41762306a36Sopenharmony_ci goto err_reg; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cierr_reg: 42362306a36Sopenharmony_ci omap_dss_put_device(ddata->in); 42462306a36Sopenharmony_ci return r; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic void td028ttec1_panel_remove(struct spi_device *spi) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); 43062306a36Sopenharmony_ci struct omap_dss_device *dssdev = &ddata->dssdev; 43162306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci dev_dbg(&ddata->spi_dev->dev, "%s\n", __func__); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci omapdss_unregister_display(dssdev); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci td028ttec1_panel_disable(dssdev); 43862306a36Sopenharmony_ci td028ttec1_panel_disconnect(dssdev); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci omap_dss_put_device(in); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic const struct of_device_id td028ttec1_of_match[] = { 44462306a36Sopenharmony_ci { .compatible = "omapdss,tpo,td028ttec1", }, 44562306a36Sopenharmony_ci /* keep to not break older DTB */ 44662306a36Sopenharmony_ci { .compatible = "omapdss,toppoly,td028ttec1", }, 44762306a36Sopenharmony_ci {}, 44862306a36Sopenharmony_ci}; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, td028ttec1_of_match); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic const struct spi_device_id td028ttec1_ids[] = { 45362306a36Sopenharmony_ci { "toppoly,td028ttec1", 0 }, 45462306a36Sopenharmony_ci { "tpo,td028ttec1", 0}, 45562306a36Sopenharmony_ci { /* sentinel */ } 45662306a36Sopenharmony_ci}; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, td028ttec1_ids); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic struct spi_driver td028ttec1_spi_driver = { 46162306a36Sopenharmony_ci .probe = td028ttec1_panel_probe, 46262306a36Sopenharmony_ci .remove = td028ttec1_panel_remove, 46362306a36Sopenharmony_ci .id_table = td028ttec1_ids, 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci .driver = { 46662306a36Sopenharmony_ci .name = "panel-tpo-td028ttec1", 46762306a36Sopenharmony_ci .of_match_table = td028ttec1_of_match, 46862306a36Sopenharmony_ci .suppress_bind_attrs = true, 46962306a36Sopenharmony_ci }, 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cimodule_spi_driver(td028ttec1_spi_driver); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ciMODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>"); 47562306a36Sopenharmony_ciMODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver"); 47662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 477