162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Toppoly TD028TTEC1 Panel Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019 Texas Instruments Incorporated 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on the omapdrm-specific panel-tpo-td028ttec1 driver 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 1062306a36Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Neo 1973 code (jbt6k74.c): 1362306a36Sopenharmony_ci * Copyright (C) 2006-2007 OpenMoko, Inc. 1462306a36Sopenharmony_ci * Author: Harald Welte <laforge@openmoko.org> 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Ported and adapted from Neo 1973 U-Boot by: 1762306a36Sopenharmony_ci * H. Nikolaus Schaller <hns@goldelico.com> 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/spi/spi.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <drm/drm_connector.h> 2562306a36Sopenharmony_ci#include <drm/drm_modes.h> 2662306a36Sopenharmony_ci#include <drm/drm_panel.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define JBT_COMMAND 0x000 2962306a36Sopenharmony_ci#define JBT_DATA 0x100 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define JBT_REG_SLEEP_IN 0x10 3262306a36Sopenharmony_ci#define JBT_REG_SLEEP_OUT 0x11 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define JBT_REG_DISPLAY_OFF 0x28 3562306a36Sopenharmony_ci#define JBT_REG_DISPLAY_ON 0x29 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define JBT_REG_RGB_FORMAT 0x3a 3862306a36Sopenharmony_ci#define JBT_REG_QUAD_RATE 0x3b 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define JBT_REG_POWER_ON_OFF 0xb0 4162306a36Sopenharmony_ci#define JBT_REG_BOOSTER_OP 0xb1 4262306a36Sopenharmony_ci#define JBT_REG_BOOSTER_MODE 0xb2 4362306a36Sopenharmony_ci#define JBT_REG_BOOSTER_FREQ 0xb3 4462306a36Sopenharmony_ci#define JBT_REG_OPAMP_SYSCLK 0xb4 4562306a36Sopenharmony_ci#define JBT_REG_VSC_VOLTAGE 0xb5 4662306a36Sopenharmony_ci#define JBT_REG_VCOM_VOLTAGE 0xb6 4762306a36Sopenharmony_ci#define JBT_REG_EXT_DISPL 0xb7 4862306a36Sopenharmony_ci#define JBT_REG_OUTPUT_CONTROL 0xb8 4962306a36Sopenharmony_ci#define JBT_REG_DCCLK_DCEV 0xb9 5062306a36Sopenharmony_ci#define JBT_REG_DISPLAY_MODE1 0xba 5162306a36Sopenharmony_ci#define JBT_REG_DISPLAY_MODE2 0xbb 5262306a36Sopenharmony_ci#define JBT_REG_DISPLAY_MODE 0xbc 5362306a36Sopenharmony_ci#define JBT_REG_ASW_SLEW 0xbd 5462306a36Sopenharmony_ci#define JBT_REG_DUMMY_DISPLAY 0xbe 5562306a36Sopenharmony_ci#define JBT_REG_DRIVE_SYSTEM 0xbf 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define JBT_REG_SLEEP_OUT_FR_A 0xc0 5862306a36Sopenharmony_ci#define JBT_REG_SLEEP_OUT_FR_B 0xc1 5962306a36Sopenharmony_ci#define JBT_REG_SLEEP_OUT_FR_C 0xc2 6062306a36Sopenharmony_ci#define JBT_REG_SLEEP_IN_LCCNT_D 0xc3 6162306a36Sopenharmony_ci#define JBT_REG_SLEEP_IN_LCCNT_E 0xc4 6262306a36Sopenharmony_ci#define JBT_REG_SLEEP_IN_LCCNT_F 0xc5 6362306a36Sopenharmony_ci#define JBT_REG_SLEEP_IN_LCCNT_G 0xc6 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define JBT_REG_GAMMA1_FINE_1 0xc7 6662306a36Sopenharmony_ci#define JBT_REG_GAMMA1_FINE_2 0xc8 6762306a36Sopenharmony_ci#define JBT_REG_GAMMA1_INCLINATION 0xc9 6862306a36Sopenharmony_ci#define JBT_REG_GAMMA1_BLUE_OFFSET 0xca 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define JBT_REG_BLANK_CONTROL 0xcf 7162306a36Sopenharmony_ci#define JBT_REG_BLANK_TH_TV 0xd0 7262306a36Sopenharmony_ci#define JBT_REG_CKV_ON_OFF 0xd1 7362306a36Sopenharmony_ci#define JBT_REG_CKV_1_2 0xd2 7462306a36Sopenharmony_ci#define JBT_REG_OEV_TIMING 0xd3 7562306a36Sopenharmony_ci#define JBT_REG_ASW_TIMING_1 0xd4 7662306a36Sopenharmony_ci#define JBT_REG_ASW_TIMING_2 0xd5 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define JBT_REG_HCLOCK_VGA 0xec 7962306a36Sopenharmony_ci#define JBT_REG_HCLOCK_QVGA 0xed 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistruct td028ttec1_panel { 8262306a36Sopenharmony_ci struct drm_panel panel; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci struct spi_device *spi; 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define to_td028ttec1_device(p) container_of(p, struct td028ttec1_panel, panel) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * noinline_for_stack so we don't get multiple copies of tx_buf 9162306a36Sopenharmony_ci * on the stack in case of gcc-plugin-structleak 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_cistatic int noinline_for_stack 9462306a36Sopenharmony_cijbt_ret_write_0(struct td028ttec1_panel *lcd, u8 reg, int *err) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct spi_device *spi = lcd->spi; 9762306a36Sopenharmony_ci u16 tx_buf = JBT_COMMAND | reg; 9862306a36Sopenharmony_ci int ret; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (err && *err) 10162306a36Sopenharmony_ci return *err; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ret = spi_write(spi, (u8 *)&tx_buf, sizeof(tx_buf)); 10462306a36Sopenharmony_ci if (ret < 0) { 10562306a36Sopenharmony_ci dev_err(&spi->dev, "%s: SPI write failed: %d\n", __func__, ret); 10662306a36Sopenharmony_ci if (err) 10762306a36Sopenharmony_ci *err = ret; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return ret; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int noinline_for_stack 11462306a36Sopenharmony_cijbt_reg_write_1(struct td028ttec1_panel *lcd, 11562306a36Sopenharmony_ci u8 reg, u8 data, int *err) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct spi_device *spi = lcd->spi; 11862306a36Sopenharmony_ci u16 tx_buf[2]; 11962306a36Sopenharmony_ci int ret; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (err && *err) 12262306a36Sopenharmony_ci return *err; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci tx_buf[0] = JBT_COMMAND | reg; 12562306a36Sopenharmony_ci tx_buf[1] = JBT_DATA | data; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ret = spi_write(spi, (u8 *)tx_buf, sizeof(tx_buf)); 12862306a36Sopenharmony_ci if (ret < 0) { 12962306a36Sopenharmony_ci dev_err(&spi->dev, "%s: SPI write failed: %d\n", __func__, ret); 13062306a36Sopenharmony_ci if (err) 13162306a36Sopenharmony_ci *err = ret; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int noinline_for_stack 13862306a36Sopenharmony_cijbt_reg_write_2(struct td028ttec1_panel *lcd, 13962306a36Sopenharmony_ci u8 reg, u16 data, int *err) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct spi_device *spi = lcd->spi; 14262306a36Sopenharmony_ci u16 tx_buf[3]; 14362306a36Sopenharmony_ci int ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (err && *err) 14662306a36Sopenharmony_ci return *err; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci tx_buf[0] = JBT_COMMAND | reg; 14962306a36Sopenharmony_ci tx_buf[1] = JBT_DATA | (data >> 8); 15062306a36Sopenharmony_ci tx_buf[2] = JBT_DATA | (data & 0xff); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci ret = spi_write(spi, (u8 *)tx_buf, sizeof(tx_buf)); 15362306a36Sopenharmony_ci if (ret < 0) { 15462306a36Sopenharmony_ci dev_err(&spi->dev, "%s: SPI write failed: %d\n", __func__, ret); 15562306a36Sopenharmony_ci if (err) 15662306a36Sopenharmony_ci *err = ret; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return ret; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int td028ttec1_prepare(struct drm_panel *panel) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct td028ttec1_panel *lcd = to_td028ttec1_device(panel); 16562306a36Sopenharmony_ci unsigned int i; 16662306a36Sopenharmony_ci int ret = 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Three times command zero */ 16962306a36Sopenharmony_ci for (i = 0; i < 3; ++i) { 17062306a36Sopenharmony_ci jbt_ret_write_0(lcd, 0x00, &ret); 17162306a36Sopenharmony_ci usleep_range(1000, 2000); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* deep standby out */ 17562306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x17, &ret); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* RGB I/F on, RAM write off, QVGA through, SIGCON enable */ 17862306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE, 0x80, &ret); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Quad mode off */ 18162306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_QUAD_RATE, 0x00, &ret); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* AVDD on, XVDD on */ 18462306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x16, &ret); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* Output control */ 18762306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_OUTPUT_CONTROL, 0xfff9, &ret); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Sleep mode off */ 19062306a36Sopenharmony_ci jbt_ret_write_0(lcd, JBT_REG_SLEEP_OUT, &ret); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* at this point we have like 50% grey */ 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* initialize register set */ 19562306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE1, 0x01, &ret); 19662306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE2, 0x00, &ret); 19762306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_RGB_FORMAT, 0x60, &ret); 19862306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_DRIVE_SYSTEM, 0x10, &ret); 19962306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_BOOSTER_OP, 0x56, &ret); 20062306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_BOOSTER_MODE, 0x33, &ret); 20162306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_BOOSTER_FREQ, 0x11, &ret); 20262306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_BOOSTER_FREQ, 0x11, &ret); 20362306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_OPAMP_SYSCLK, 0x02, &ret); 20462306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_VSC_VOLTAGE, 0x2b, &ret); 20562306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_VCOM_VOLTAGE, 0x40, &ret); 20662306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_EXT_DISPL, 0x03, &ret); 20762306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_DCCLK_DCEV, 0x04, &ret); 20862306a36Sopenharmony_ci /* 20962306a36Sopenharmony_ci * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement 21062306a36Sopenharmony_ci * to avoid red / blue flicker 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_ASW_SLEW, 0x04, &ret); 21362306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_DUMMY_DISPLAY, 0x00, &ret); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_A, 0x11, &ret); 21662306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_B, 0x11, &ret); 21762306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_C, 0x11, &ret); 21862306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040, &ret); 21962306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0, &ret); 22062306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020, &ret); 22162306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0, &ret); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_GAMMA1_FINE_1, 0x5533, &ret); 22462306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_GAMMA1_FINE_2, 0x00, &ret); 22562306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_GAMMA1_INCLINATION, 0x00, &ret); 22662306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00, &ret); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_HCLOCK_VGA, 0x1f0, &ret); 22962306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_BLANK_CONTROL, 0x02, &ret); 23062306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_BLANK_TH_TV, 0x0804, &ret); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_CKV_ON_OFF, 0x01, &ret); 23362306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_CKV_1_2, 0x0000, &ret); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_OEV_TIMING, 0x0d0e, &ret); 23662306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_ASW_TIMING_1, 0x11a4, &ret); 23762306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_ASW_TIMING_2, 0x0e, &ret); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return ret; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int td028ttec1_enable(struct drm_panel *panel) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct td028ttec1_panel *lcd = to_td028ttec1_device(panel); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return jbt_ret_write_0(lcd, JBT_REG_DISPLAY_ON, NULL); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int td028ttec1_disable(struct drm_panel *panel) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct td028ttec1_panel *lcd = to_td028ttec1_device(panel); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci jbt_ret_write_0(lcd, JBT_REG_DISPLAY_OFF, NULL); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int td028ttec1_unprepare(struct drm_panel *panel) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct td028ttec1_panel *lcd = to_td028ttec1_device(panel); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci jbt_reg_write_2(lcd, JBT_REG_OUTPUT_CONTROL, 0x8002, NULL); 26362306a36Sopenharmony_ci jbt_ret_write_0(lcd, JBT_REG_SLEEP_IN, NULL); 26462306a36Sopenharmony_ci jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x00, NULL); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic const struct drm_display_mode td028ttec1_mode = { 27062306a36Sopenharmony_ci .clock = 22153, 27162306a36Sopenharmony_ci .hdisplay = 480, 27262306a36Sopenharmony_ci .hsync_start = 480 + 24, 27362306a36Sopenharmony_ci .hsync_end = 480 + 24 + 8, 27462306a36Sopenharmony_ci .htotal = 480 + 24 + 8 + 8, 27562306a36Sopenharmony_ci .vdisplay = 640, 27662306a36Sopenharmony_ci .vsync_start = 640 + 4, 27762306a36Sopenharmony_ci .vsync_end = 640 + 4 + 2, 27862306a36Sopenharmony_ci .vtotal = 640 + 4 + 2 + 2, 27962306a36Sopenharmony_ci .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 28062306a36Sopenharmony_ci .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 28162306a36Sopenharmony_ci .width_mm = 43, 28262306a36Sopenharmony_ci .height_mm = 58, 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int td028ttec1_get_modes(struct drm_panel *panel, 28662306a36Sopenharmony_ci struct drm_connector *connector) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct drm_display_mode *mode; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, &td028ttec1_mode); 29162306a36Sopenharmony_ci if (!mode) 29262306a36Sopenharmony_ci return -ENOMEM; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci drm_mode_set_name(mode); 29562306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci connector->display_info.width_mm = td028ttec1_mode.width_mm; 29862306a36Sopenharmony_ci connector->display_info.height_mm = td028ttec1_mode.height_mm; 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * FIXME: According to the datasheet sync signals are sampled on the 30162306a36Sopenharmony_ci * rising edge of the clock, but the code running on the OpenMoko Neo 30262306a36Sopenharmony_ci * FreeRunner and Neo 1973 indicates sampling on the falling edge. This 30362306a36Sopenharmony_ci * should be tested on a real device. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH 30662306a36Sopenharmony_ci | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE 30762306a36Sopenharmony_ci | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return 1; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic const struct drm_panel_funcs td028ttec1_funcs = { 31362306a36Sopenharmony_ci .prepare = td028ttec1_prepare, 31462306a36Sopenharmony_ci .enable = td028ttec1_enable, 31562306a36Sopenharmony_ci .disable = td028ttec1_disable, 31662306a36Sopenharmony_ci .unprepare = td028ttec1_unprepare, 31762306a36Sopenharmony_ci .get_modes = td028ttec1_get_modes, 31862306a36Sopenharmony_ci}; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int td028ttec1_probe(struct spi_device *spi) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct td028ttec1_panel *lcd; 32362306a36Sopenharmony_ci int ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); 32662306a36Sopenharmony_ci if (!lcd) 32762306a36Sopenharmony_ci return -ENOMEM; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci spi_set_drvdata(spi, lcd); 33062306a36Sopenharmony_ci lcd->spi = spi; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci spi->mode = SPI_MODE_3; 33362306a36Sopenharmony_ci spi->bits_per_word = 9; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci ret = spi_setup(spi); 33662306a36Sopenharmony_ci if (ret < 0) { 33762306a36Sopenharmony_ci dev_err(&spi->dev, "failed to setup SPI: %d\n", ret); 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci drm_panel_init(&lcd->panel, &lcd->spi->dev, &td028ttec1_funcs, 34262306a36Sopenharmony_ci DRM_MODE_CONNECTOR_DPI); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci ret = drm_panel_of_backlight(&lcd->panel); 34562306a36Sopenharmony_ci if (ret) 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci drm_panel_add(&lcd->panel); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic void td028ttec1_remove(struct spi_device *spi) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct td028ttec1_panel *lcd = spi_get_drvdata(spi); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci drm_panel_remove(&lcd->panel); 35862306a36Sopenharmony_ci drm_panel_disable(&lcd->panel); 35962306a36Sopenharmony_ci drm_panel_unprepare(&lcd->panel); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic const struct of_device_id td028ttec1_of_match[] = { 36362306a36Sopenharmony_ci { .compatible = "tpo,td028ttec1", }, 36462306a36Sopenharmony_ci /* DT backward compatibility. */ 36562306a36Sopenharmony_ci { .compatible = "toppoly,td028ttec1", }, 36662306a36Sopenharmony_ci { /* sentinel */ }, 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, td028ttec1_of_match); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic const struct spi_device_id td028ttec1_ids[] = { 37262306a36Sopenharmony_ci { "td028ttec1", 0 }, 37362306a36Sopenharmony_ci { /* sentinel */ } 37462306a36Sopenharmony_ci}; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, td028ttec1_ids); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic struct spi_driver td028ttec1_driver = { 37962306a36Sopenharmony_ci .probe = td028ttec1_probe, 38062306a36Sopenharmony_ci .remove = td028ttec1_remove, 38162306a36Sopenharmony_ci .id_table = td028ttec1_ids, 38262306a36Sopenharmony_ci .driver = { 38362306a36Sopenharmony_ci .name = "panel-tpo-td028ttec1", 38462306a36Sopenharmony_ci .of_match_table = td028ttec1_of_match, 38562306a36Sopenharmony_ci }, 38662306a36Sopenharmony_ci}; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cimodule_spi_driver(td028ttec1_driver); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ciMODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>"); 39162306a36Sopenharmony_ciMODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver"); 39262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 393