[go: nahoru, domu]

1/*
2 * ld9040 AMOLED LCD panel driver.
3 *
4 * Copyright (c) 2011 Samsung Electronics
5 * Author: Donghwa Lee  <dh09.lee@samsung.com>
6 * Derived from drivers/video/backlight/s6e63m0.c
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 */
13
14#include <linux/backlight.h>
15#include <linux/delay.h>
16#include <linux/fb.h>
17#include <linux/gpio.h>
18#include <linux/interrupt.h>
19#include <linux/irq.h>
20#include <linux/kernel.h>
21#include <linux/lcd.h>
22#include <linux/module.h>
23#include <linux/regulator/consumer.h>
24#include <linux/spi/spi.h>
25#include <linux/wait.h>
26
27#include "ld9040_gamma.h"
28
29#define SLEEPMSEC		0x1000
30#define ENDDEF			0x2000
31#define	DEFMASK			0xFF00
32#define COMMAND_ONLY		0xFE
33#define DATA_ONLY		0xFF
34
35#define MIN_BRIGHTNESS		0
36#define MAX_BRIGHTNESS		24
37
38struct ld9040 {
39	struct device			*dev;
40	struct spi_device		*spi;
41	unsigned int			power;
42	unsigned int			current_brightness;
43
44	struct lcd_device		*ld;
45	struct backlight_device		*bd;
46	struct lcd_platform_data	*lcd_pd;
47
48	struct mutex			lock;
49	bool  enabled;
50};
51
52static struct regulator_bulk_data supplies[] = {
53	{ .supply = "vdd3", },
54	{ .supply = "vci", },
55};
56
57static void ld9040_regulator_enable(struct ld9040 *lcd)
58{
59	int ret = 0;
60	struct lcd_platform_data *pd = NULL;
61
62	pd = lcd->lcd_pd;
63	mutex_lock(&lcd->lock);
64	if (!lcd->enabled) {
65		ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
66		if (ret)
67			goto out;
68
69		lcd->enabled = true;
70	}
71	msleep(pd->power_on_delay);
72out:
73	mutex_unlock(&lcd->lock);
74}
75
76static void ld9040_regulator_disable(struct ld9040 *lcd)
77{
78	int ret = 0;
79
80	mutex_lock(&lcd->lock);
81	if (lcd->enabled) {
82		ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
83		if (ret)
84			goto out;
85
86		lcd->enabled = false;
87	}
88out:
89	mutex_unlock(&lcd->lock);
90}
91
92static const unsigned short seq_swreset[] = {
93	0x01, COMMAND_ONLY,
94	ENDDEF, 0x00
95};
96
97static const unsigned short seq_user_setting[] = {
98	0xF0, 0x5A,
99
100	DATA_ONLY, 0x5A,
101	ENDDEF, 0x00
102};
103
104static const unsigned short seq_elvss_on[] = {
105	0xB1, 0x0D,
106
107	DATA_ONLY, 0x00,
108	DATA_ONLY, 0x16,
109	ENDDEF, 0x00
110};
111
112static const unsigned short seq_gtcon[] = {
113	0xF7, 0x09,
114
115	DATA_ONLY, 0x00,
116	DATA_ONLY, 0x00,
117	ENDDEF, 0x00
118};
119
120static const unsigned short seq_panel_condition[] = {
121	0xF8, 0x05,
122
123	DATA_ONLY, 0x65,
124	DATA_ONLY, 0x96,
125	DATA_ONLY, 0x71,
126	DATA_ONLY, 0x7D,
127	DATA_ONLY, 0x19,
128	DATA_ONLY, 0x3B,
129	DATA_ONLY, 0x0D,
130	DATA_ONLY, 0x19,
131	DATA_ONLY, 0x7E,
132	DATA_ONLY, 0x0D,
133	DATA_ONLY, 0xE2,
134	DATA_ONLY, 0x00,
135	DATA_ONLY, 0x00,
136	DATA_ONLY, 0x7E,
137	DATA_ONLY, 0x7D,
138	DATA_ONLY, 0x07,
139	DATA_ONLY, 0x07,
140	DATA_ONLY, 0x20,
141	DATA_ONLY, 0x20,
142	DATA_ONLY, 0x20,
143	DATA_ONLY, 0x02,
144	DATA_ONLY, 0x02,
145	ENDDEF, 0x00
146};
147
148static const unsigned short seq_gamma_set1[] = {
149	0xF9, 0x00,
150
151	DATA_ONLY, 0xA7,
152	DATA_ONLY, 0xB4,
153	DATA_ONLY, 0xAE,
154	DATA_ONLY, 0xBF,
155	DATA_ONLY, 0x00,
156	DATA_ONLY, 0x91,
157	DATA_ONLY, 0x00,
158	DATA_ONLY, 0xB2,
159	DATA_ONLY, 0xB4,
160	DATA_ONLY, 0xAA,
161	DATA_ONLY, 0xBB,
162	DATA_ONLY, 0x00,
163	DATA_ONLY, 0xAC,
164	DATA_ONLY, 0x00,
165	DATA_ONLY, 0xB3,
166	DATA_ONLY, 0xB1,
167	DATA_ONLY, 0xAA,
168	DATA_ONLY, 0xBC,
169	DATA_ONLY, 0x00,
170	DATA_ONLY, 0xB3,
171	ENDDEF, 0x00
172};
173
174static const unsigned short seq_gamma_ctrl[] = {
175	0xFB, 0x02,
176
177	DATA_ONLY, 0x5A,
178	ENDDEF, 0x00
179};
180
181static const unsigned short seq_gamma_start[] = {
182	0xF9, COMMAND_ONLY,
183
184	ENDDEF, 0x00
185};
186
187static const unsigned short seq_apon[] = {
188	0xF3, 0x00,
189
190	DATA_ONLY, 0x00,
191	DATA_ONLY, 0x00,
192	DATA_ONLY, 0x0A,
193	DATA_ONLY, 0x02,
194	ENDDEF, 0x00
195};
196
197static const unsigned short seq_display_ctrl[] = {
198	0xF2, 0x02,
199
200	DATA_ONLY, 0x08,
201	DATA_ONLY, 0x08,
202	DATA_ONLY, 0x10,
203	DATA_ONLY, 0x10,
204	ENDDEF, 0x00
205};
206
207static const unsigned short seq_manual_pwr[] = {
208	0xB0, 0x04,
209	ENDDEF, 0x00
210};
211
212static const unsigned short seq_pwr_ctrl[] = {
213	0xF4, 0x0A,
214
215	DATA_ONLY, 0x87,
216	DATA_ONLY, 0x25,
217	DATA_ONLY, 0x6A,
218	DATA_ONLY, 0x44,
219	DATA_ONLY, 0x02,
220	DATA_ONLY, 0x88,
221	ENDDEF, 0x00
222};
223
224static const unsigned short seq_sleep_out[] = {
225	0x11, COMMAND_ONLY,
226	ENDDEF, 0x00
227};
228
229static const unsigned short seq_sleep_in[] = {
230	0x10, COMMAND_ONLY,
231	ENDDEF, 0x00
232};
233
234static const unsigned short seq_display_on[] = {
235	0x29, COMMAND_ONLY,
236	ENDDEF, 0x00
237};
238
239static const unsigned short seq_display_off[] = {
240	0x28, COMMAND_ONLY,
241	ENDDEF, 0x00
242};
243
244static const unsigned short seq_vci1_1st_en[] = {
245	0xF3, 0x10,
246
247	DATA_ONLY, 0x00,
248	DATA_ONLY, 0x00,
249	DATA_ONLY, 0x00,
250	DATA_ONLY, 0x02,
251	ENDDEF, 0x00
252};
253
254static const unsigned short seq_vl1_en[] = {
255	0xF3, 0x11,
256
257	DATA_ONLY, 0x00,
258	DATA_ONLY, 0x00,
259	DATA_ONLY, 0x00,
260	DATA_ONLY, 0x02,
261	ENDDEF, 0x00
262};
263
264static const unsigned short seq_vl2_en[] = {
265	0xF3, 0x13,
266
267	DATA_ONLY, 0x00,
268	DATA_ONLY, 0x00,
269	DATA_ONLY, 0x00,
270	DATA_ONLY, 0x02,
271	ENDDEF, 0x00
272};
273
274static const unsigned short seq_vci1_2nd_en[] = {
275	0xF3, 0x33,
276
277	DATA_ONLY, 0x00,
278	DATA_ONLY, 0x00,
279	DATA_ONLY, 0x00,
280	DATA_ONLY, 0x02,
281	ENDDEF, 0x00
282};
283
284static const unsigned short seq_vl3_en[] = {
285	0xF3, 0x37,
286
287	DATA_ONLY, 0x00,
288	DATA_ONLY, 0x00,
289	DATA_ONLY, 0x00,
290	DATA_ONLY, 0x02,
291	ENDDEF, 0x00
292};
293
294static const unsigned short seq_vreg1_amp_en[] = {
295	0xF3, 0x37,
296
297	DATA_ONLY, 0x01,
298	DATA_ONLY, 0x00,
299	DATA_ONLY, 0x00,
300	DATA_ONLY, 0x02,
301	ENDDEF, 0x00
302};
303
304static const unsigned short seq_vgh_amp_en[] = {
305	0xF3, 0x37,
306
307	DATA_ONLY, 0x11,
308	DATA_ONLY, 0x00,
309	DATA_ONLY, 0x00,
310	DATA_ONLY, 0x02,
311	ENDDEF, 0x00
312};
313
314static const unsigned short seq_vgl_amp_en[] = {
315	0xF3, 0x37,
316
317	DATA_ONLY, 0x31,
318	DATA_ONLY, 0x00,
319	DATA_ONLY, 0x00,
320	DATA_ONLY, 0x02,
321	ENDDEF, 0x00
322};
323
324static const unsigned short seq_vmos_amp_en[] = {
325	0xF3, 0x37,
326
327	DATA_ONLY, 0xB1,
328	DATA_ONLY, 0x00,
329	DATA_ONLY, 0x00,
330	DATA_ONLY, 0x03,
331	ENDDEF, 0x00
332};
333
334static const unsigned short seq_vint_amp_en[] = {
335	0xF3, 0x37,
336
337	DATA_ONLY, 0xF1,
338	/* DATA_ONLY, 0x71,	VMOS/VBL/VBH not used */
339	DATA_ONLY, 0x00,
340	DATA_ONLY, 0x00,
341	DATA_ONLY, 0x03,
342	/* DATA_ONLY, 0x02,	VMOS/VBL/VBH not used */
343	ENDDEF, 0x00
344};
345
346static const unsigned short seq_vbh_amp_en[] = {
347	0xF3, 0x37,
348
349	DATA_ONLY, 0xF9,
350	DATA_ONLY, 0x00,
351	DATA_ONLY, 0x00,
352	DATA_ONLY, 0x03,
353	ENDDEF, 0x00
354};
355
356static const unsigned short seq_vbl_amp_en[] = {
357	0xF3, 0x37,
358
359	DATA_ONLY, 0xFD,
360	DATA_ONLY, 0x00,
361	DATA_ONLY, 0x00,
362	DATA_ONLY, 0x03,
363	ENDDEF, 0x00
364};
365
366static const unsigned short seq_gam_amp_en[] = {
367	0xF3, 0x37,
368
369	DATA_ONLY, 0xFF,
370	/* DATA_ONLY, 0x73,	VMOS/VBL/VBH not used */
371	DATA_ONLY, 0x00,
372	DATA_ONLY, 0x00,
373	DATA_ONLY, 0x03,
374	/* DATA_ONLY, 0x02,	VMOS/VBL/VBH not used */
375	ENDDEF, 0x00
376};
377
378static const unsigned short seq_sd_amp_en[] = {
379	0xF3, 0x37,
380
381	DATA_ONLY, 0xFF,
382	/* DATA_ONLY, 0x73,	VMOS/VBL/VBH not used */
383	DATA_ONLY, 0x80,
384	DATA_ONLY, 0x00,
385	DATA_ONLY, 0x03,
386	/* DATA_ONLY, 0x02,	VMOS/VBL/VBH not used */
387	ENDDEF, 0x00
388};
389
390static const unsigned short seq_gls_en[] = {
391	0xF3, 0x37,
392
393	DATA_ONLY, 0xFF,
394	/* DATA_ONLY, 0x73,	VMOS/VBL/VBH not used */
395	DATA_ONLY, 0x81,
396	DATA_ONLY, 0x00,
397	DATA_ONLY, 0x03,
398	/* DATA_ONLY, 0x02,	VMOS/VBL/VBH not used */
399	ENDDEF, 0x00
400};
401
402static const unsigned short seq_els_en[] = {
403	0xF3, 0x37,
404
405	DATA_ONLY, 0xFF,
406	/* DATA_ONLY, 0x73,	VMOS/VBL/VBH not used */
407	DATA_ONLY, 0x83,
408	DATA_ONLY, 0x00,
409	DATA_ONLY, 0x03,
410	/* DATA_ONLY, 0x02,	VMOS/VBL/VBH not used */
411	ENDDEF, 0x00
412};
413
414static const unsigned short seq_el_on[] = {
415	0xF3, 0x37,
416
417	DATA_ONLY, 0xFF,
418	/* DATA_ONLY, 0x73,	VMOS/VBL/VBH not used */
419	DATA_ONLY, 0x87,
420	DATA_ONLY, 0x00,
421	DATA_ONLY, 0x03,
422	/* DATA_ONLY, 0x02,	VMOS/VBL/VBH not used */
423	ENDDEF, 0x00
424};
425
426static int ld9040_spi_write_byte(struct ld9040 *lcd, int addr, int data)
427{
428	u16 buf[1];
429	struct spi_message msg;
430
431	struct spi_transfer xfer = {
432		.len		= 2,
433		.tx_buf		= buf,
434	};
435
436	buf[0] = (addr << 8) | data;
437
438	spi_message_init(&msg);
439	spi_message_add_tail(&xfer, &msg);
440
441	return spi_sync(lcd->spi, &msg);
442}
443
444static int ld9040_spi_write(struct ld9040 *lcd, unsigned char address,
445	unsigned char command)
446{
447	int ret = 0;
448
449	if (address != DATA_ONLY)
450		ret = ld9040_spi_write_byte(lcd, 0x0, address);
451	if (command != COMMAND_ONLY)
452		ret = ld9040_spi_write_byte(lcd, 0x1, command);
453
454	return ret;
455}
456
457static int ld9040_panel_send_sequence(struct ld9040 *lcd,
458	const unsigned short *wbuf)
459{
460	int ret = 0, i = 0;
461
462	while ((wbuf[i] & DEFMASK) != ENDDEF) {
463		if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
464			ret = ld9040_spi_write(lcd, wbuf[i], wbuf[i+1]);
465			if (ret)
466				break;
467		} else {
468			msleep(wbuf[i+1]);
469		}
470		i += 2;
471	}
472
473	return ret;
474}
475
476static int _ld9040_gamma_ctl(struct ld9040 *lcd, const unsigned int *gamma)
477{
478	unsigned int i = 0;
479	int ret = 0;
480
481	/* start gamma table updating. */
482	ret = ld9040_panel_send_sequence(lcd, seq_gamma_start);
483	if (ret) {
484		dev_err(lcd->dev, "failed to disable gamma table updating.\n");
485		goto gamma_err;
486	}
487
488	for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
489		ret = ld9040_spi_write(lcd, DATA_ONLY, gamma[i]);
490		if (ret) {
491			dev_err(lcd->dev, "failed to set gamma table.\n");
492			goto gamma_err;
493		}
494	}
495
496	/* update gamma table. */
497	ret = ld9040_panel_send_sequence(lcd, seq_gamma_ctrl);
498	if (ret)
499		dev_err(lcd->dev, "failed to update gamma table.\n");
500
501gamma_err:
502	return ret;
503}
504
505static int ld9040_gamma_ctl(struct ld9040 *lcd, int gamma)
506{
507	return _ld9040_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
508}
509
510static int ld9040_ldi_init(struct ld9040 *lcd)
511{
512	int ret, i;
513	static const unsigned short *init_seq[] = {
514		seq_user_setting,
515		seq_panel_condition,
516		seq_display_ctrl,
517		seq_manual_pwr,
518		seq_elvss_on,
519		seq_gtcon,
520		seq_gamma_set1,
521		seq_gamma_ctrl,
522		seq_sleep_out,
523	};
524
525	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
526		ret = ld9040_panel_send_sequence(lcd, init_seq[i]);
527		/* workaround: minimum delay time for transferring CMD */
528		usleep_range(300, 310);
529		if (ret)
530			break;
531	}
532
533	return ret;
534}
535
536static int ld9040_ldi_enable(struct ld9040 *lcd)
537{
538	return ld9040_panel_send_sequence(lcd, seq_display_on);
539}
540
541static int ld9040_ldi_disable(struct ld9040 *lcd)
542{
543	int ret;
544
545	ret = ld9040_panel_send_sequence(lcd, seq_display_off);
546	ret = ld9040_panel_send_sequence(lcd, seq_sleep_in);
547
548	return ret;
549}
550
551static int ld9040_power_is_on(int power)
552{
553	return power <= FB_BLANK_NORMAL;
554}
555
556static int ld9040_power_on(struct ld9040 *lcd)
557{
558	int ret = 0;
559	struct lcd_platform_data *pd;
560
561	pd = lcd->lcd_pd;
562
563	/* lcd power on */
564	ld9040_regulator_enable(lcd);
565
566	if (!pd->reset) {
567		dev_err(lcd->dev, "reset is NULL.\n");
568		return -EINVAL;
569	}
570
571	pd->reset(lcd->ld);
572	msleep(pd->reset_delay);
573
574	ret = ld9040_ldi_init(lcd);
575	if (ret) {
576		dev_err(lcd->dev, "failed to initialize ldi.\n");
577		return ret;
578	}
579
580	ret = ld9040_ldi_enable(lcd);
581	if (ret) {
582		dev_err(lcd->dev, "failed to enable ldi.\n");
583		return ret;
584	}
585
586	return 0;
587}
588
589static int ld9040_power_off(struct ld9040 *lcd)
590{
591	int ret;
592	struct lcd_platform_data *pd;
593
594	pd = lcd->lcd_pd;
595
596	ret = ld9040_ldi_disable(lcd);
597	if (ret) {
598		dev_err(lcd->dev, "lcd setting failed.\n");
599		return -EIO;
600	}
601
602	msleep(pd->power_off_delay);
603
604	/* lcd power off */
605	ld9040_regulator_disable(lcd);
606
607	return 0;
608}
609
610static int ld9040_power(struct ld9040 *lcd, int power)
611{
612	int ret = 0;
613
614	if (ld9040_power_is_on(power) && !ld9040_power_is_on(lcd->power))
615		ret = ld9040_power_on(lcd);
616	else if (!ld9040_power_is_on(power) && ld9040_power_is_on(lcd->power))
617		ret = ld9040_power_off(lcd);
618
619	if (!ret)
620		lcd->power = power;
621
622	return ret;
623}
624
625static int ld9040_set_power(struct lcd_device *ld, int power)
626{
627	struct ld9040 *lcd = lcd_get_data(ld);
628
629	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
630		power != FB_BLANK_NORMAL) {
631		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
632		return -EINVAL;
633	}
634
635	return ld9040_power(lcd, power);
636}
637
638static int ld9040_get_power(struct lcd_device *ld)
639{
640	struct ld9040 *lcd = lcd_get_data(ld);
641
642	return lcd->power;
643}
644
645static int ld9040_set_brightness(struct backlight_device *bd)
646{
647	int ret = 0, brightness = bd->props.brightness;
648	struct ld9040 *lcd = bl_get_data(bd);
649
650	if (brightness < MIN_BRIGHTNESS ||
651		brightness > bd->props.max_brightness) {
652		dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
653			MIN_BRIGHTNESS, MAX_BRIGHTNESS);
654		return -EINVAL;
655	}
656
657	ret = ld9040_gamma_ctl(lcd, bd->props.brightness);
658	if (ret) {
659		dev_err(&bd->dev, "lcd brightness setting failed.\n");
660		return -EIO;
661	}
662
663	return ret;
664}
665
666static struct lcd_ops ld9040_lcd_ops = {
667	.set_power = ld9040_set_power,
668	.get_power = ld9040_get_power,
669};
670
671static const struct backlight_ops ld9040_backlight_ops  = {
672	.update_status = ld9040_set_brightness,
673};
674
675static int ld9040_probe(struct spi_device *spi)
676{
677	int ret = 0;
678	struct ld9040 *lcd = NULL;
679	struct lcd_device *ld = NULL;
680	struct backlight_device *bd = NULL;
681	struct backlight_properties props;
682
683	lcd = devm_kzalloc(&spi->dev, sizeof(struct ld9040), GFP_KERNEL);
684	if (!lcd)
685		return -ENOMEM;
686
687	/* ld9040 lcd panel uses 3-wire 9bits SPI Mode. */
688	spi->bits_per_word = 9;
689
690	ret = spi_setup(spi);
691	if (ret < 0) {
692		dev_err(&spi->dev, "spi setup failed.\n");
693		return ret;
694	}
695
696	lcd->spi = spi;
697	lcd->dev = &spi->dev;
698
699	lcd->lcd_pd = dev_get_platdata(&spi->dev);
700	if (!lcd->lcd_pd) {
701		dev_err(&spi->dev, "platform data is NULL.\n");
702		return -EINVAL;
703	}
704
705	mutex_init(&lcd->lock);
706
707	ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
708	if (ret) {
709		dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
710		return ret;
711	}
712
713	ld = devm_lcd_device_register(&spi->dev, "ld9040", &spi->dev, lcd,
714					&ld9040_lcd_ops);
715	if (IS_ERR(ld))
716		return PTR_ERR(ld);
717
718	lcd->ld = ld;
719
720	memset(&props, 0, sizeof(struct backlight_properties));
721	props.type = BACKLIGHT_RAW;
722	props.max_brightness = MAX_BRIGHTNESS;
723
724	bd = devm_backlight_device_register(&spi->dev, "ld9040-bl", &spi->dev,
725					lcd, &ld9040_backlight_ops, &props);
726	if (IS_ERR(bd))
727		return PTR_ERR(bd);
728
729	bd->props.brightness = MAX_BRIGHTNESS;
730	lcd->bd = bd;
731
732	/*
733	 * if lcd panel was on from bootloader like u-boot then
734	 * do not lcd on.
735	 */
736	if (!lcd->lcd_pd->lcd_enabled) {
737		/*
738		 * if lcd panel was off from bootloader then
739		 * current lcd status is powerdown and then
740		 * it enables lcd panel.
741		 */
742		lcd->power = FB_BLANK_POWERDOWN;
743
744		ld9040_power(lcd, FB_BLANK_UNBLANK);
745	} else {
746		lcd->power = FB_BLANK_UNBLANK;
747	}
748
749	spi_set_drvdata(spi, lcd);
750
751	dev_info(&spi->dev, "ld9040 panel driver has been probed.\n");
752	return 0;
753}
754
755static int ld9040_remove(struct spi_device *spi)
756{
757	struct ld9040 *lcd = spi_get_drvdata(spi);
758
759	ld9040_power(lcd, FB_BLANK_POWERDOWN);
760	return 0;
761}
762
763#ifdef CONFIG_PM_SLEEP
764static int ld9040_suspend(struct device *dev)
765{
766	struct ld9040 *lcd = dev_get_drvdata(dev);
767
768	dev_dbg(dev, "lcd->power = %d\n", lcd->power);
769
770	/*
771	 * when lcd panel is suspend, lcd panel becomes off
772	 * regardless of status.
773	 */
774	return ld9040_power(lcd, FB_BLANK_POWERDOWN);
775}
776
777static int ld9040_resume(struct device *dev)
778{
779	struct ld9040 *lcd = dev_get_drvdata(dev);
780
781	lcd->power = FB_BLANK_POWERDOWN;
782
783	return ld9040_power(lcd, FB_BLANK_UNBLANK);
784}
785#endif
786
787static SIMPLE_DEV_PM_OPS(ld9040_pm_ops, ld9040_suspend, ld9040_resume);
788
789/* Power down all displays on reboot, poweroff or halt. */
790static void ld9040_shutdown(struct spi_device *spi)
791{
792	struct ld9040 *lcd = spi_get_drvdata(spi);
793
794	ld9040_power(lcd, FB_BLANK_POWERDOWN);
795}
796
797static struct spi_driver ld9040_driver = {
798	.driver = {
799		.name	= "ld9040",
800		.owner	= THIS_MODULE,
801		.pm	= &ld9040_pm_ops,
802	},
803	.probe		= ld9040_probe,
804	.remove		= ld9040_remove,
805	.shutdown	= ld9040_shutdown,
806};
807
808module_spi_driver(ld9040_driver);
809
810MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
811MODULE_DESCRIPTION("ld9040 LCD Driver");
812MODULE_LICENSE("GPL");
813