18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * lms501kf03 TFT LCD panel driver.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Samsung Electronics Co., Ltd.
68c2ecf20Sopenharmony_ci * Author: Jingoo Han  <jg1.han@samsung.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/backlight.h>
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/fb.h>
128c2ecf20Sopenharmony_ci#include <linux/lcd.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
158c2ecf20Sopenharmony_ci#include <linux/wait.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define COMMAND_ONLY		0x00
188c2ecf20Sopenharmony_ci#define DATA_ONLY		0x01
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct lms501kf03 {
218c2ecf20Sopenharmony_ci	struct device			*dev;
228c2ecf20Sopenharmony_ci	struct spi_device		*spi;
238c2ecf20Sopenharmony_ci	unsigned int			power;
248c2ecf20Sopenharmony_ci	struct lcd_device		*ld;
258c2ecf20Sopenharmony_ci	struct lcd_platform_data	*lcd_pd;
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic const unsigned char seq_password[] = {
298c2ecf20Sopenharmony_ci	0xb9, 0xff, 0x83, 0x69,
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic const unsigned char seq_power[] = {
338c2ecf20Sopenharmony_ci	0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28,
348c2ecf20Sopenharmony_ci	0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic const unsigned char seq_display[] = {
388c2ecf20Sopenharmony_ci	0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00,
398c2ecf20Sopenharmony_ci	0x00, 0x00, 0x03, 0x03, 0x00, 0x01,
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic const unsigned char seq_rgb_if[] = {
438c2ecf20Sopenharmony_ci	0xb3, 0x09,
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic const unsigned char seq_display_inv[] = {
478c2ecf20Sopenharmony_ci	0xb4, 0x01, 0x08, 0x77, 0x0e, 0x06,
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic const unsigned char seq_vcom[] = {
518c2ecf20Sopenharmony_ci	0xb6, 0x4c, 0x2e,
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic const unsigned char seq_gate[] = {
558c2ecf20Sopenharmony_ci	0xd5, 0x00, 0x05, 0x03, 0x29, 0x01, 0x07, 0x17, 0x68, 0x13,
568c2ecf20Sopenharmony_ci	0x37, 0x20, 0x31, 0x8a, 0x46, 0x9b, 0x57, 0x13, 0x02, 0x75,
578c2ecf20Sopenharmony_ci	0xb9, 0x64, 0xa8, 0x07, 0x0f, 0x04, 0x07,
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic const unsigned char seq_panel[] = {
618c2ecf20Sopenharmony_ci	0xcc, 0x02,
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const unsigned char seq_col_mod[] = {
658c2ecf20Sopenharmony_ci	0x3a, 0x77,
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic const unsigned char seq_w_gamma[] = {
698c2ecf20Sopenharmony_ci	0xe0, 0x00, 0x04, 0x09, 0x0f, 0x1f, 0x3f, 0x1f, 0x2f, 0x0a,
708c2ecf20Sopenharmony_ci	0x0f, 0x10, 0x16, 0x18, 0x16, 0x17, 0x0d, 0x15, 0x00, 0x04,
718c2ecf20Sopenharmony_ci	0x09, 0x0f, 0x38, 0x3f, 0x20, 0x39, 0x0a, 0x0f, 0x10, 0x16,
728c2ecf20Sopenharmony_ci	0x18, 0x16, 0x17, 0x0d, 0x15,
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic const unsigned char seq_rgb_gamma[] = {
768c2ecf20Sopenharmony_ci	0xc1, 0x01, 0x03, 0x07, 0x0f, 0x1a, 0x22, 0x2c, 0x33, 0x3c,
778c2ecf20Sopenharmony_ci	0x46, 0x4f, 0x58, 0x60, 0x69, 0x71, 0x79, 0x82, 0x89, 0x92,
788c2ecf20Sopenharmony_ci	0x9a, 0xa1, 0xa9, 0xb1, 0xb9, 0xc1, 0xc9, 0xcf, 0xd6, 0xde,
798c2ecf20Sopenharmony_ci	0xe5, 0xec, 0xf3, 0xf9, 0xff, 0xdd, 0x39, 0x07, 0x1c, 0xcb,
808c2ecf20Sopenharmony_ci	0xab, 0x5f, 0x49, 0x80, 0x03, 0x07, 0x0f, 0x19, 0x20, 0x2a,
818c2ecf20Sopenharmony_ci	0x31, 0x39, 0x42, 0x4b, 0x53, 0x5b, 0x63, 0x6b, 0x73, 0x7b,
828c2ecf20Sopenharmony_ci	0x83, 0x8a, 0x92, 0x9b, 0xa2, 0xaa, 0xb2, 0xba, 0xc2, 0xca,
838c2ecf20Sopenharmony_ci	0xd0, 0xd8, 0xe1, 0xe8, 0xf0, 0xf8, 0xff, 0xf7, 0xd8, 0xbe,
848c2ecf20Sopenharmony_ci	0xa7, 0x39, 0x40, 0x85, 0x8c, 0xc0, 0x04, 0x07, 0x0c, 0x17,
858c2ecf20Sopenharmony_ci	0x1c, 0x23, 0x2b, 0x34, 0x3b, 0x43, 0x4c, 0x54, 0x5b, 0x63,
868c2ecf20Sopenharmony_ci	0x6a, 0x73, 0x7a, 0x82, 0x8a, 0x91, 0x98, 0xa1, 0xa8, 0xb0,
878c2ecf20Sopenharmony_ci	0xb7, 0xc1, 0xc9, 0xcf, 0xd9, 0xe3, 0xea, 0xf4, 0xff, 0x00,
888c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic const unsigned char seq_sleep_out[] = {
928c2ecf20Sopenharmony_ci	0x11,
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic const unsigned char seq_display_on[] = {
968c2ecf20Sopenharmony_ci	0x29,
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic const unsigned char seq_display_off[] = {
1008c2ecf20Sopenharmony_ci	0x10,
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int lms501kf03_spi_write_byte(struct lms501kf03 *lcd, int addr, int data)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	u16 buf[1];
1068c2ecf20Sopenharmony_ci	struct spi_message msg;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	struct spi_transfer xfer = {
1098c2ecf20Sopenharmony_ci		.len		= 2,
1108c2ecf20Sopenharmony_ci		.tx_buf		= buf,
1118c2ecf20Sopenharmony_ci	};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	buf[0] = (addr << 8) | data;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	spi_message_init(&msg);
1168c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer, &msg);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return spi_sync(lcd->spi, &msg);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int lms501kf03_spi_write(struct lms501kf03 *lcd, unsigned char address,
1228c2ecf20Sopenharmony_ci				unsigned char command)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	return lms501kf03_spi_write_byte(lcd, address, command);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int lms501kf03_panel_send_sequence(struct lms501kf03 *lcd,
1288c2ecf20Sopenharmony_ci					const unsigned char *wbuf,
1298c2ecf20Sopenharmony_ci					unsigned int len)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	int ret = 0, i = 0;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	while (i < len) {
1348c2ecf20Sopenharmony_ci		if (i == 0)
1358c2ecf20Sopenharmony_ci			ret = lms501kf03_spi_write(lcd, COMMAND_ONLY, wbuf[i]);
1368c2ecf20Sopenharmony_ci		else
1378c2ecf20Sopenharmony_ci			ret = lms501kf03_spi_write(lcd, DATA_ONLY, wbuf[i]);
1388c2ecf20Sopenharmony_ci		if (ret)
1398c2ecf20Sopenharmony_ci			break;
1408c2ecf20Sopenharmony_ci		i += 1;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return ret;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int lms501kf03_ldi_init(struct lms501kf03 *lcd)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int ret, i;
1498c2ecf20Sopenharmony_ci	static const unsigned char *init_seq[] = {
1508c2ecf20Sopenharmony_ci		seq_password,
1518c2ecf20Sopenharmony_ci		seq_power,
1528c2ecf20Sopenharmony_ci		seq_display,
1538c2ecf20Sopenharmony_ci		seq_rgb_if,
1548c2ecf20Sopenharmony_ci		seq_display_inv,
1558c2ecf20Sopenharmony_ci		seq_vcom,
1568c2ecf20Sopenharmony_ci		seq_gate,
1578c2ecf20Sopenharmony_ci		seq_panel,
1588c2ecf20Sopenharmony_ci		seq_col_mod,
1598c2ecf20Sopenharmony_ci		seq_w_gamma,
1608c2ecf20Sopenharmony_ci		seq_rgb_gamma,
1618c2ecf20Sopenharmony_ci		seq_sleep_out,
1628c2ecf20Sopenharmony_ci	};
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	static const unsigned int size_seq[] = {
1658c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_password),
1668c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_power),
1678c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_display),
1688c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_rgb_if),
1698c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_display_inv),
1708c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_vcom),
1718c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_gate),
1728c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_panel),
1738c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_col_mod),
1748c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_w_gamma),
1758c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_rgb_gamma),
1768c2ecf20Sopenharmony_ci		ARRAY_SIZE(seq_sleep_out),
1778c2ecf20Sopenharmony_ci	};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
1808c2ecf20Sopenharmony_ci		ret = lms501kf03_panel_send_sequence(lcd, init_seq[i],
1818c2ecf20Sopenharmony_ci						size_seq[i]);
1828c2ecf20Sopenharmony_ci		if (ret)
1838c2ecf20Sopenharmony_ci			break;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci	/*
1868c2ecf20Sopenharmony_ci	 * According to the datasheet, 120ms delay time is required.
1878c2ecf20Sopenharmony_ci	 * After sleep out sequence, command is blocked for 120ms.
1888c2ecf20Sopenharmony_ci	 * Thus, LDI should wait for 120ms.
1898c2ecf20Sopenharmony_ci	 */
1908c2ecf20Sopenharmony_ci	msleep(120);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return ret;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int lms501kf03_ldi_enable(struct lms501kf03 *lcd)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	return lms501kf03_panel_send_sequence(lcd, seq_display_on,
1988c2ecf20Sopenharmony_ci					ARRAY_SIZE(seq_display_on));
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic int lms501kf03_ldi_disable(struct lms501kf03 *lcd)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	return lms501kf03_panel_send_sequence(lcd, seq_display_off,
2048c2ecf20Sopenharmony_ci					ARRAY_SIZE(seq_display_off));
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic int lms501kf03_power_is_on(int power)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	return (power) <= FB_BLANK_NORMAL;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int lms501kf03_power_on(struct lms501kf03 *lcd)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	int ret = 0;
2158c2ecf20Sopenharmony_ci	struct lcd_platform_data *pd;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	pd = lcd->lcd_pd;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (!pd->power_on) {
2208c2ecf20Sopenharmony_ci		dev_err(lcd->dev, "power_on is NULL.\n");
2218c2ecf20Sopenharmony_ci		return -EINVAL;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	pd->power_on(lcd->ld, 1);
2258c2ecf20Sopenharmony_ci	msleep(pd->power_on_delay);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!pd->reset) {
2288c2ecf20Sopenharmony_ci		dev_err(lcd->dev, "reset is NULL.\n");
2298c2ecf20Sopenharmony_ci		return -EINVAL;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	pd->reset(lcd->ld);
2338c2ecf20Sopenharmony_ci	msleep(pd->reset_delay);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	ret = lms501kf03_ldi_init(lcd);
2368c2ecf20Sopenharmony_ci	if (ret) {
2378c2ecf20Sopenharmony_ci		dev_err(lcd->dev, "failed to initialize ldi.\n");
2388c2ecf20Sopenharmony_ci		return ret;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	ret = lms501kf03_ldi_enable(lcd);
2428c2ecf20Sopenharmony_ci	if (ret) {
2438c2ecf20Sopenharmony_ci		dev_err(lcd->dev, "failed to enable ldi.\n");
2448c2ecf20Sopenharmony_ci		return ret;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int lms501kf03_power_off(struct lms501kf03 *lcd)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	int ret = 0;
2538c2ecf20Sopenharmony_ci	struct lcd_platform_data *pd;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	pd = lcd->lcd_pd;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	ret = lms501kf03_ldi_disable(lcd);
2588c2ecf20Sopenharmony_ci	if (ret) {
2598c2ecf20Sopenharmony_ci		dev_err(lcd->dev, "lcd setting failed.\n");
2608c2ecf20Sopenharmony_ci		return -EIO;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	msleep(pd->power_off_delay);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	pd->power_on(lcd->ld, 0);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return 0;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic int lms501kf03_power(struct lms501kf03 *lcd, int power)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	int ret = 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (lms501kf03_power_is_on(power) &&
2758c2ecf20Sopenharmony_ci		!lms501kf03_power_is_on(lcd->power))
2768c2ecf20Sopenharmony_ci		ret = lms501kf03_power_on(lcd);
2778c2ecf20Sopenharmony_ci	else if (!lms501kf03_power_is_on(power) &&
2788c2ecf20Sopenharmony_ci		lms501kf03_power_is_on(lcd->power))
2798c2ecf20Sopenharmony_ci		ret = lms501kf03_power_off(lcd);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (!ret)
2828c2ecf20Sopenharmony_ci		lcd->power = power;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return ret;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic int lms501kf03_get_power(struct lcd_device *ld)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct lms501kf03 *lcd = lcd_get_data(ld);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return lcd->power;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int lms501kf03_set_power(struct lcd_device *ld, int power)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct lms501kf03 *lcd = lcd_get_data(ld);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
2998c2ecf20Sopenharmony_ci		power != FB_BLANK_NORMAL) {
3008c2ecf20Sopenharmony_ci		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
3018c2ecf20Sopenharmony_ci		return -EINVAL;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return lms501kf03_power(lcd, power);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic struct lcd_ops lms501kf03_lcd_ops = {
3088c2ecf20Sopenharmony_ci	.get_power = lms501kf03_get_power,
3098c2ecf20Sopenharmony_ci	.set_power = lms501kf03_set_power,
3108c2ecf20Sopenharmony_ci};
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int lms501kf03_probe(struct spi_device *spi)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct lms501kf03 *lcd = NULL;
3158c2ecf20Sopenharmony_ci	struct lcd_device *ld = NULL;
3168c2ecf20Sopenharmony_ci	int ret = 0;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	lcd = devm_kzalloc(&spi->dev, sizeof(struct lms501kf03), GFP_KERNEL);
3198c2ecf20Sopenharmony_ci	if (!lcd)
3208c2ecf20Sopenharmony_ci		return -ENOMEM;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* lms501kf03 lcd panel uses 3-wire 9-bit SPI Mode. */
3238c2ecf20Sopenharmony_ci	spi->bits_per_word = 9;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	ret = spi_setup(spi);
3268c2ecf20Sopenharmony_ci	if (ret < 0) {
3278c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "spi setup failed.\n");
3288c2ecf20Sopenharmony_ci		return ret;
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	lcd->spi = spi;
3328c2ecf20Sopenharmony_ci	lcd->dev = &spi->dev;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	lcd->lcd_pd = dev_get_platdata(&spi->dev);
3358c2ecf20Sopenharmony_ci	if (!lcd->lcd_pd) {
3368c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "platform data is NULL\n");
3378c2ecf20Sopenharmony_ci		return -EINVAL;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	ld = devm_lcd_device_register(&spi->dev, "lms501kf03", &spi->dev, lcd,
3418c2ecf20Sopenharmony_ci					&lms501kf03_lcd_ops);
3428c2ecf20Sopenharmony_ci	if (IS_ERR(ld))
3438c2ecf20Sopenharmony_ci		return PTR_ERR(ld);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	lcd->ld = ld;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!lcd->lcd_pd->lcd_enabled) {
3488c2ecf20Sopenharmony_ci		/*
3498c2ecf20Sopenharmony_ci		 * if lcd panel was off from bootloader then
3508c2ecf20Sopenharmony_ci		 * current lcd status is powerdown and then
3518c2ecf20Sopenharmony_ci		 * it enables lcd panel.
3528c2ecf20Sopenharmony_ci		 */
3538c2ecf20Sopenharmony_ci		lcd->power = FB_BLANK_POWERDOWN;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		lms501kf03_power(lcd, FB_BLANK_UNBLANK);
3568c2ecf20Sopenharmony_ci	} else {
3578c2ecf20Sopenharmony_ci		lcd->power = FB_BLANK_UNBLANK;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	spi_set_drvdata(spi, lcd);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	dev_info(&spi->dev, "lms501kf03 panel driver has been probed.\n");
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return 0;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic int lms501kf03_remove(struct spi_device *spi)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	struct lms501kf03 *lcd = spi_get_drvdata(spi);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
3728c2ecf20Sopenharmony_ci	return 0;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
3768c2ecf20Sopenharmony_cistatic int lms501kf03_suspend(struct device *dev)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct lms501kf03 *lcd = dev_get_drvdata(dev);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	dev_dbg(dev, "lcd->power = %d\n", lcd->power);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/*
3838c2ecf20Sopenharmony_ci	 * when lcd panel is suspend, lcd panel becomes off
3848c2ecf20Sopenharmony_ci	 * regardless of status.
3858c2ecf20Sopenharmony_ci	 */
3868c2ecf20Sopenharmony_ci	return lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int lms501kf03_resume(struct device *dev)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct lms501kf03 *lcd = dev_get_drvdata(dev);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	lcd->power = FB_BLANK_POWERDOWN;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return lms501kf03_power(lcd, FB_BLANK_UNBLANK);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci#endif
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(lms501kf03_pm_ops, lms501kf03_suspend,
4008c2ecf20Sopenharmony_ci			lms501kf03_resume);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic void lms501kf03_shutdown(struct spi_device *spi)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct lms501kf03 *lcd = spi_get_drvdata(spi);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic struct spi_driver lms501kf03_driver = {
4108c2ecf20Sopenharmony_ci	.driver = {
4118c2ecf20Sopenharmony_ci		.name	= "lms501kf03",
4128c2ecf20Sopenharmony_ci		.pm	= &lms501kf03_pm_ops,
4138c2ecf20Sopenharmony_ci	},
4148c2ecf20Sopenharmony_ci	.probe		= lms501kf03_probe,
4158c2ecf20Sopenharmony_ci	.remove		= lms501kf03_remove,
4168c2ecf20Sopenharmony_ci	.shutdown	= lms501kf03_shutdown,
4178c2ecf20Sopenharmony_ci};
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cimodule_spi_driver(lms501kf03_driver);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
4228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("lms501kf03 LCD Driver");
4238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
424