18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ld9040 AMOLED LCD drm_panel driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014 Samsung Electronics Co., Ltd 68c2ecf20Sopenharmony_ci * Derived from drivers/video/backlight/ld9040.c 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Andrzej Hajda <a.hajda@samsung.com> 98c2ecf20Sopenharmony_ci*/ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 168c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <video/mipi_display.h> 198c2ecf20Sopenharmony_ci#include <video/of_videomode.h> 208c2ecf20Sopenharmony_ci#include <video/videomode.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <drm/drm_modes.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Manufacturer Command Set */ 268c2ecf20Sopenharmony_ci#define MCS_MANPWR 0xb0 278c2ecf20Sopenharmony_ci#define MCS_ELVSS_ON 0xb1 288c2ecf20Sopenharmony_ci#define MCS_USER_SETTING 0xf0 298c2ecf20Sopenharmony_ci#define MCS_DISPCTL 0xf2 308c2ecf20Sopenharmony_ci#define MCS_POWER_CTRL 0xf4 318c2ecf20Sopenharmony_ci#define MCS_GTCON 0xf7 328c2ecf20Sopenharmony_ci#define MCS_PANEL_CONDITION 0xf8 338c2ecf20Sopenharmony_ci#define MCS_GAMMA_SET1 0xf9 348c2ecf20Sopenharmony_ci#define MCS_GAMMA_CTRL 0xfb 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* array of gamma tables for gamma value 2.2 */ 378c2ecf20Sopenharmony_cistatic u8 const ld9040_gammas[25][22] = { 388c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x13, 0xb2, 0xba, 0xd2, 0x00, 0x30, 0x00, 0xaf, 0xc0, 398c2ecf20Sopenharmony_ci 0xb8, 0xcd, 0x00, 0x3d, 0x00, 0xa8, 0xb8, 0xb7, 0xcd, 0x00, 0x44 }, 408c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x13, 0xb9, 0xb9, 0xd0, 0x00, 0x3c, 0x00, 0xaf, 0xbf, 418c2ecf20Sopenharmony_ci 0xb6, 0xcb, 0x00, 0x4b, 0x00, 0xa8, 0xb9, 0xb5, 0xcc, 0x00, 0x52 }, 428c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x13, 0xba, 0xb9, 0xcd, 0x00, 0x41, 0x00, 0xb0, 0xbe, 438c2ecf20Sopenharmony_ci 0xb5, 0xc9, 0x00, 0x51, 0x00, 0xa9, 0xb9, 0xb5, 0xca, 0x00, 0x57 }, 448c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x13, 0xb9, 0xb8, 0xcd, 0x00, 0x46, 0x00, 0xb1, 0xbc, 458c2ecf20Sopenharmony_ci 0xb5, 0xc8, 0x00, 0x56, 0x00, 0xaa, 0xb8, 0xb4, 0xc9, 0x00, 0x5d }, 468c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x13, 0xba, 0xb8, 0xcb, 0x00, 0x4b, 0x00, 0xb3, 0xbc, 478c2ecf20Sopenharmony_ci 0xb4, 0xc7, 0x00, 0x5c, 0x00, 0xac, 0xb8, 0xb4, 0xc8, 0x00, 0x62 }, 488c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x13, 0xbb, 0xb7, 0xca, 0x00, 0x4f, 0x00, 0xb4, 0xbb, 498c2ecf20Sopenharmony_ci 0xb3, 0xc7, 0x00, 0x60, 0x00, 0xad, 0xb8, 0xb4, 0xc7, 0x00, 0x67 }, 508c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x47, 0xba, 0xb6, 0xca, 0x00, 0x53, 0x00, 0xb5, 0xbb, 518c2ecf20Sopenharmony_ci 0xb3, 0xc6, 0x00, 0x65, 0x00, 0xae, 0xb8, 0xb3, 0xc7, 0x00, 0x6c }, 528c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x71, 0xbb, 0xb5, 0xc8, 0x00, 0x57, 0x00, 0xb5, 0xbb, 538c2ecf20Sopenharmony_ci 0xb0, 0xc5, 0x00, 0x6a, 0x00, 0xae, 0xb9, 0xb1, 0xc6, 0x00, 0x70 }, 548c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x7b, 0xbb, 0xb4, 0xc8, 0x00, 0x5b, 0x00, 0xb5, 0xba, 558c2ecf20Sopenharmony_ci 0xb1, 0xc4, 0x00, 0x6e, 0x00, 0xae, 0xb9, 0xb0, 0xc5, 0x00, 0x75 }, 568c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x82, 0xba, 0xb4, 0xc7, 0x00, 0x5f, 0x00, 0xb5, 0xba, 578c2ecf20Sopenharmony_ci 0xb0, 0xc3, 0x00, 0x72, 0x00, 0xae, 0xb8, 0xb0, 0xc3, 0x00, 0x7a }, 588c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x89, 0xba, 0xb3, 0xc8, 0x00, 0x62, 0x00, 0xb6, 0xba, 598c2ecf20Sopenharmony_ci 0xaf, 0xc3, 0x00, 0x76, 0x00, 0xaf, 0xb7, 0xae, 0xc4, 0x00, 0x7e }, 608c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x8b, 0xb9, 0xb3, 0xc7, 0x00, 0x65, 0x00, 0xb7, 0xb8, 618c2ecf20Sopenharmony_ci 0xaf, 0xc3, 0x00, 0x7a, 0x00, 0x80, 0xb6, 0xae, 0xc4, 0x00, 0x81 }, 628c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x93, 0xba, 0xb3, 0xc5, 0x00, 0x69, 0x00, 0xb8, 0xb9, 638c2ecf20Sopenharmony_ci 0xae, 0xc1, 0x00, 0x7f, 0x00, 0xb0, 0xb6, 0xae, 0xc3, 0x00, 0x85 }, 648c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x97, 0xba, 0xb2, 0xc5, 0x00, 0x6c, 0x00, 0xb8, 0xb8, 658c2ecf20Sopenharmony_ci 0xae, 0xc1, 0x00, 0x82, 0x00, 0xb0, 0xb6, 0xae, 0xc2, 0x00, 0x89 }, 668c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x9a, 0xba, 0xb1, 0xc4, 0x00, 0x6f, 0x00, 0xb8, 0xb8, 678c2ecf20Sopenharmony_ci 0xad, 0xc0, 0x00, 0x86, 0x00, 0xb0, 0xb7, 0xad, 0xc0, 0x00, 0x8d }, 688c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x9c, 0xb9, 0xb0, 0xc4, 0x00, 0x72, 0x00, 0xb8, 0xb8, 698c2ecf20Sopenharmony_ci 0xac, 0xbf, 0x00, 0x8a, 0x00, 0xb0, 0xb6, 0xac, 0xc0, 0x00, 0x91 }, 708c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0x9e, 0xba, 0xb0, 0xc2, 0x00, 0x75, 0x00, 0xb9, 0xb8, 718c2ecf20Sopenharmony_ci 0xab, 0xbe, 0x00, 0x8e, 0x00, 0xb0, 0xb6, 0xac, 0xbf, 0x00, 0x94 }, 728c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0xa0, 0xb9, 0xaf, 0xc3, 0x00, 0x77, 0x00, 0xb9, 0xb7, 738c2ecf20Sopenharmony_ci 0xab, 0xbe, 0x00, 0x90, 0x00, 0xb0, 0xb6, 0xab, 0xbf, 0x00, 0x97 }, 748c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0xa2, 0xb9, 0xaf, 0xc2, 0x00, 0x7a, 0x00, 0xb9, 0xb7, 758c2ecf20Sopenharmony_ci 0xaa, 0xbd, 0x00, 0x94, 0x00, 0xb0, 0xb5, 0xab, 0xbf, 0x00, 0x9a }, 768c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0xa4, 0xb9, 0xaf, 0xc1, 0x00, 0x7d, 0x00, 0xb9, 0xb6, 778c2ecf20Sopenharmony_ci 0xaa, 0xbb, 0x00, 0x97, 0x00, 0xb1, 0xb5, 0xaa, 0xbf, 0x00, 0x9d }, 788c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0xa4, 0xb8, 0xb0, 0xbf, 0x00, 0x80, 0x00, 0xb8, 0xb6, 798c2ecf20Sopenharmony_ci 0xaa, 0xbc, 0x00, 0x9a, 0x00, 0xb0, 0xb5, 0xab, 0xbd, 0x00, 0xa0 }, 808c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0xa8, 0xb8, 0xae, 0xbe, 0x00, 0x84, 0x00, 0xb9, 0xb7, 818c2ecf20Sopenharmony_ci 0xa8, 0xbc, 0x00, 0x9d, 0x00, 0xb2, 0xb5, 0xaa, 0xbc, 0x00, 0xa4 }, 828c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0xa9, 0xb6, 0xad, 0xbf, 0x00, 0x86, 0x00, 0xb8, 0xb5, 838c2ecf20Sopenharmony_ci 0xa8, 0xbc, 0x00, 0xa0, 0x00, 0xb3, 0xb3, 0xa9, 0xbc, 0x00, 0xa7 }, 848c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0xa9, 0xb7, 0xae, 0xbd, 0x00, 0x89, 0x00, 0xb7, 0xb6, 858c2ecf20Sopenharmony_ci 0xa8, 0xba, 0x00, 0xa4, 0x00, 0xb1, 0xb4, 0xaa, 0xbb, 0x00, 0xaa }, 868c2ecf20Sopenharmony_ci { 0xf9, 0x00, 0xa7, 0xb4, 0xae, 0xbf, 0x00, 0x91, 0x00, 0xb2, 0xb4, 878c2ecf20Sopenharmony_ci 0xaa, 0xbb, 0x00, 0xac, 0x00, 0xb3, 0xb1, 0xaa, 0xbc, 0x00, 0xb3 }, 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistruct ld9040 { 918c2ecf20Sopenharmony_ci struct device *dev; 928c2ecf20Sopenharmony_ci struct drm_panel panel; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci struct regulator_bulk_data supplies[2]; 958c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 968c2ecf20Sopenharmony_ci u32 power_on_delay; 978c2ecf20Sopenharmony_ci u32 reset_delay; 988c2ecf20Sopenharmony_ci struct videomode vm; 998c2ecf20Sopenharmony_ci u32 width_mm; 1008c2ecf20Sopenharmony_ci u32 height_mm; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci int brightness; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* This field is tested by functions directly accessing bus before 1058c2ecf20Sopenharmony_ci * transfer, transfer is skipped if it is set. In case of transfer 1068c2ecf20Sopenharmony_ci * failure or unexpected response the field is set to error value. 1078c2ecf20Sopenharmony_ci * Such construct allows to eliminate many checks in higher level 1088c2ecf20Sopenharmony_ci * functions. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci int error; 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic inline struct ld9040 *panel_to_ld9040(struct drm_panel *panel) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci return container_of(panel, struct ld9040, panel); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int ld9040_clear_error(struct ld9040 *ctx) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci int ret = ctx->error; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ctx->error = 0; 1238c2ecf20Sopenharmony_ci return ret; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int ld9040_spi_write_word(struct ld9040 *ctx, u16 data) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct spi_device *spi = to_spi_device(ctx->dev); 1298c2ecf20Sopenharmony_ci struct spi_transfer xfer = { 1308c2ecf20Sopenharmony_ci .len = 2, 1318c2ecf20Sopenharmony_ci .tx_buf = &data, 1328c2ecf20Sopenharmony_ci }; 1338c2ecf20Sopenharmony_ci struct spi_message msg; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci spi_message_init(&msg); 1368c2ecf20Sopenharmony_ci spi_message_add_tail(&xfer, &msg); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return spi_sync(spi, &msg); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void ld9040_dcs_write(struct ld9040 *ctx, const u8 *data, size_t len) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci int ret = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (ctx->error < 0 || len == 0) 1468c2ecf20Sopenharmony_ci return; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data); 1498c2ecf20Sopenharmony_ci ret = ld9040_spi_write_word(ctx, *data); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci while (!ret && --len) { 1528c2ecf20Sopenharmony_ci ++data; 1538c2ecf20Sopenharmony_ci ret = ld9040_spi_write_word(ctx, *data | 0x100); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (ret) { 1578c2ecf20Sopenharmony_ci dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, 1588c2ecf20Sopenharmony_ci (int)len, data); 1598c2ecf20Sopenharmony_ci ctx->error = ret; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci usleep_range(300, 310); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci#define ld9040_dcs_write_seq_static(ctx, seq...) \ 1668c2ecf20Sopenharmony_ci({\ 1678c2ecf20Sopenharmony_ci static const u8 d[] = { seq };\ 1688c2ecf20Sopenharmony_ci ld9040_dcs_write(ctx, d, ARRAY_SIZE(d));\ 1698c2ecf20Sopenharmony_ci}) 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void ld9040_brightness_set(struct ld9040 *ctx) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci ld9040_dcs_write(ctx, ld9040_gammas[ctx->brightness], 1748c2ecf20Sopenharmony_ci ARRAY_SIZE(ld9040_gammas[ctx->brightness])); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MCS_GAMMA_CTRL, 0x02, 0x5a); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void ld9040_init(struct ld9040 *ctx) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MCS_USER_SETTING, 0x5a, 0x5a); 1828c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MCS_PANEL_CONDITION, 1838c2ecf20Sopenharmony_ci 0x05, 0x65, 0x96, 0x71, 0x7d, 0x19, 0x3b, 0x0d, 1848c2ecf20Sopenharmony_ci 0x19, 0x7e, 0x0d, 0xe2, 0x00, 0x00, 0x7e, 0x7d, 1858c2ecf20Sopenharmony_ci 0x07, 0x07, 0x20, 0x20, 0x20, 0x02, 0x02); 1868c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MCS_DISPCTL, 1878c2ecf20Sopenharmony_ci 0x02, 0x08, 0x08, 0x10, 0x10); 1888c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MCS_MANPWR, 0x04); 1898c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MCS_POWER_CTRL, 1908c2ecf20Sopenharmony_ci 0x0a, 0x87, 0x25, 0x6a, 0x44, 0x02, 0x88); 1918c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 0x0d, 0x00, 0x16); 1928c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MCS_GTCON, 0x09, 0x00, 0x00); 1938c2ecf20Sopenharmony_ci ld9040_brightness_set(ctx); 1948c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); 1958c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int ld9040_power_on(struct ld9040 *ctx) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 2038c2ecf20Sopenharmony_ci if (ret < 0) 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci msleep(ctx->power_on_delay); 2078c2ecf20Sopenharmony_ci gpiod_set_value(ctx->reset_gpio, 0); 2088c2ecf20Sopenharmony_ci msleep(ctx->reset_delay); 2098c2ecf20Sopenharmony_ci gpiod_set_value(ctx->reset_gpio, 1); 2108c2ecf20Sopenharmony_ci msleep(ctx->reset_delay); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int ld9040_power_off(struct ld9040 *ctx) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int ld9040_disable(struct drm_panel *panel) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int ld9040_unprepare(struct drm_panel *panel) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct ld9040 *ctx = panel_to_ld9040(panel); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci msleep(120); 2308c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF); 2318c2ecf20Sopenharmony_ci ld9040_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); 2328c2ecf20Sopenharmony_ci msleep(40); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci ld9040_clear_error(ctx); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return ld9040_power_off(ctx); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int ld9040_prepare(struct drm_panel *panel) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct ld9040 *ctx = panel_to_ld9040(panel); 2428c2ecf20Sopenharmony_ci int ret; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ret = ld9040_power_on(ctx); 2458c2ecf20Sopenharmony_ci if (ret < 0) 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ld9040_init(ctx); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci ret = ld9040_clear_error(ctx); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (ret < 0) 2538c2ecf20Sopenharmony_ci ld9040_unprepare(panel); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return ret; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int ld9040_enable(struct drm_panel *panel) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int ld9040_get_modes(struct drm_panel *panel, 2648c2ecf20Sopenharmony_ci struct drm_connector *connector) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct ld9040 *ctx = panel_to_ld9040(panel); 2678c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci mode = drm_mode_create(connector->dev); 2708c2ecf20Sopenharmony_ci if (!mode) { 2718c2ecf20Sopenharmony_ci dev_err(panel->dev, "failed to create a new display mode\n"); 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci drm_display_mode_from_videomode(&ctx->vm, mode); 2768c2ecf20Sopenharmony_ci mode->width_mm = ctx->width_mm; 2778c2ecf20Sopenharmony_ci mode->height_mm = ctx->height_mm; 2788c2ecf20Sopenharmony_ci connector->display_info.width_mm = mode->width_mm; 2798c2ecf20Sopenharmony_ci connector->display_info.height_mm = mode->height_mm; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 2828c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 1; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic const struct drm_panel_funcs ld9040_drm_funcs = { 2888c2ecf20Sopenharmony_ci .disable = ld9040_disable, 2898c2ecf20Sopenharmony_ci .unprepare = ld9040_unprepare, 2908c2ecf20Sopenharmony_ci .prepare = ld9040_prepare, 2918c2ecf20Sopenharmony_ci .enable = ld9040_enable, 2928c2ecf20Sopenharmony_ci .get_modes = ld9040_get_modes, 2938c2ecf20Sopenharmony_ci}; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int ld9040_parse_dt(struct ld9040 *ctx) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct device *dev = ctx->dev; 2988c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 2998c2ecf20Sopenharmony_ci int ret; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ret = of_get_videomode(np, &ctx->vm, 0); 3028c2ecf20Sopenharmony_ci if (ret < 0) 3038c2ecf20Sopenharmony_ci return ret; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci of_property_read_u32(np, "power-on-delay", &ctx->power_on_delay); 3068c2ecf20Sopenharmony_ci of_property_read_u32(np, "reset-delay", &ctx->reset_delay); 3078c2ecf20Sopenharmony_ci of_property_read_u32(np, "panel-width-mm", &ctx->width_mm); 3088c2ecf20Sopenharmony_ci of_property_read_u32(np, "panel-height-mm", &ctx->height_mm); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int ld9040_probe(struct spi_device *spi) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct device *dev = &spi->dev; 3168c2ecf20Sopenharmony_ci struct ld9040 *ctx; 3178c2ecf20Sopenharmony_ci int ret; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(struct ld9040), GFP_KERNEL); 3208c2ecf20Sopenharmony_ci if (!ctx) 3218c2ecf20Sopenharmony_ci return -ENOMEM; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci spi_set_drvdata(spi, ctx); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci ctx->dev = dev; 3268c2ecf20Sopenharmony_ci ctx->brightness = ARRAY_SIZE(ld9040_gammas) - 1; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ret = ld9040_parse_dt(ctx); 3298c2ecf20Sopenharmony_ci if (ret < 0) 3308c2ecf20Sopenharmony_ci return ret; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ctx->supplies[0].supply = "vdd3"; 3338c2ecf20Sopenharmony_ci ctx->supplies[1].supply = "vci"; 3348c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 3358c2ecf20Sopenharmony_ci ctx->supplies); 3368c2ecf20Sopenharmony_ci if (ret < 0) 3378c2ecf20Sopenharmony_ci return ret; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 3408c2ecf20Sopenharmony_ci if (IS_ERR(ctx->reset_gpio)) { 3418c2ecf20Sopenharmony_ci dev_err(dev, "cannot get reset-gpios %ld\n", 3428c2ecf20Sopenharmony_ci PTR_ERR(ctx->reset_gpio)); 3438c2ecf20Sopenharmony_ci return PTR_ERR(ctx->reset_gpio); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci spi->bits_per_word = 9; 3478c2ecf20Sopenharmony_ci ret = spi_setup(spi); 3488c2ecf20Sopenharmony_ci if (ret < 0) { 3498c2ecf20Sopenharmony_ci dev_err(dev, "spi setup failed.\n"); 3508c2ecf20Sopenharmony_ci return ret; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci drm_panel_init(&ctx->panel, dev, &ld9040_drm_funcs, 3548c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DPI); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci drm_panel_add(&ctx->panel); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int ld9040_remove(struct spi_device *spi) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct ld9040 *ctx = spi_get_drvdata(spi); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ld9040_power_off(ctx); 3668c2ecf20Sopenharmony_ci drm_panel_remove(&ctx->panel); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic const struct of_device_id ld9040_of_match[] = { 3728c2ecf20Sopenharmony_ci { .compatible = "samsung,ld9040" }, 3738c2ecf20Sopenharmony_ci { } 3748c2ecf20Sopenharmony_ci}; 3758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ld9040_of_match); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic const struct spi_device_id ld9040_ids[] = { 3788c2ecf20Sopenharmony_ci { "ld9040", }, 3798c2ecf20Sopenharmony_ci { /* sentinel */ } 3808c2ecf20Sopenharmony_ci}; 3818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, ld9040_ids); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic struct spi_driver ld9040_driver = { 3848c2ecf20Sopenharmony_ci .probe = ld9040_probe, 3858c2ecf20Sopenharmony_ci .remove = ld9040_remove, 3868c2ecf20Sopenharmony_ci .driver = { 3878c2ecf20Sopenharmony_ci .name = "panel-samsung-ld9040", 3888c2ecf20Sopenharmony_ci .of_match_table = ld9040_of_match, 3898c2ecf20Sopenharmony_ci }, 3908c2ecf20Sopenharmony_ci}; 3918c2ecf20Sopenharmony_cimodule_spi_driver(ld9040_driver); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); 3948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ld9040 LCD Driver"); 3958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 396