diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-11 22:19:50 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-11 22:19:50 -0400 |
commit | 6f35308c3ffa256bed183adf6f2c0c6c211ca487 (patch) | |
tree | f1aaaec5f650bdd80d30230df25bbf33e43efd5f /drivers/video | |
parent | 19ad7ae47e4ce4eb2a583e437d653a96da7897ac (diff) | |
parent | c3f8f65046127f471d0b6193a1923185b354c011 (diff) |
Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-backlight
* 'for-linus' of git://git.o-hand.com/linux-rpurdie-backlight:
backlight: Convert corgi backlight driver into a more generic driver
backlight: Add Samsung LTV350QV LCD driver
backlight: Fix cr_bllcd allocations and error paths
backlight/leds: Make two structs static
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/backlight/Kconfig | 23 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/backlight/backlight.c | 2 | ||||
-rw-r--r-- | drivers/video/backlight/corgi_bl.c | 22 | ||||
-rw-r--r-- | drivers/video/backlight/cr_bllcd.c | 35 | ||||
-rw-r--r-- | drivers/video/backlight/lcd.c | 2 | ||||
-rw-r--r-- | drivers/video/backlight/ltv350qv.c | 330 | ||||
-rw-r--r-- | drivers/video/backlight/ltv350qv.h | 95 |
8 files changed, 477 insertions, 34 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 2580f5fa2486..9609a6c676be 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig | |||
@@ -24,6 +24,18 @@ config LCD_CLASS_DEVICE | |||
24 | To have support for your specific LCD panel you will have to | 24 | To have support for your specific LCD panel you will have to |
25 | select the proper drivers which depend on this option. | 25 | select the proper drivers which depend on this option. |
26 | 26 | ||
27 | config LCD_LTV350QV | ||
28 | tristate "Samsung LTV350QV LCD Panel" | ||
29 | depends on LCD_CLASS_DEVICE && SPI_MASTER | ||
30 | default n | ||
31 | help | ||
32 | If you have a Samsung LTV350QV LCD panel, say y to include a | ||
33 | power control driver for it. The panel starts up in power | ||
34 | off state, so you need this driver in order to see any | ||
35 | output. | ||
36 | |||
37 | The LTV350QV panel is present on all ATSTK1000 boards. | ||
38 | |||
27 | # | 39 | # |
28 | # Backlight | 40 | # Backlight |
29 | # | 41 | # |
@@ -39,12 +51,13 @@ config BACKLIGHT_CLASS_DEVICE | |||
39 | select the proper drivers which depend on this option. | 51 | select the proper drivers which depend on this option. |
40 | 52 | ||
41 | config BACKLIGHT_CORGI | 53 | config BACKLIGHT_CORGI |
42 | tristate "Sharp Corgi Backlight Driver (SL Series)" | 54 | tristate "Generic (aka Sharp Corgi) Backlight Driver" |
43 | depends on BACKLIGHT_CLASS_DEVICE && PXA_SHARPSL | 55 | depends on BACKLIGHT_CLASS_DEVICE |
44 | default y | 56 | default n |
45 | help | 57 | help |
46 | If you have a Sharp Zaurus SL-C7xx, SL-Cxx00 or SL-6000x say y to enable the | 58 | Say y to enable the generic platform backlight driver previously |
47 | backlight driver. | 59 | known as the Corgi backlight driver. If you have a Sharp Zaurus |
60 | SL-C7xx, SL-Cxx00 or SL-6000x say y. Most users can say n. | ||
48 | 61 | ||
49 | config BACKLIGHT_LOCOMO | 62 | config BACKLIGHT_LOCOMO |
50 | tristate "Sharp LOCOMO LCD/Backlight Driver" | 63 | tristate "Sharp LOCOMO LCD/Backlight Driver" |
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index c6e2266f63e2..965a78b18118 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile | |||
@@ -1,6 +1,8 @@ | |||
1 | # Backlight & LCD drivers | 1 | # Backlight & LCD drivers |
2 | 2 | ||
3 | obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o | 3 | obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o |
4 | obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o | ||
5 | |||
4 | obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o | 6 | obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o |
5 | obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o | 7 | obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o |
6 | obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o | 8 | obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o |
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index b26de8cf3112..4840fe217e4d 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c | |||
@@ -164,7 +164,7 @@ static ssize_t backlight_show_actual_brightness(struct device *dev, | |||
164 | return rc; | 164 | return rc; |
165 | } | 165 | } |
166 | 166 | ||
167 | struct class *backlight_class; | 167 | static struct class *backlight_class; |
168 | 168 | ||
169 | static void bl_device_release(struct device *dev) | 169 | static void bl_device_release(struct device *dev) |
170 | { | 170 | { |
diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c index ce00e18a4e5d..4d4d037e3ec9 100644 --- a/drivers/video/backlight/corgi_bl.c +++ b/drivers/video/backlight/corgi_bl.c | |||
@@ -18,13 +18,11 @@ | |||
18 | #include <linux/mutex.h> | 18 | #include <linux/mutex.h> |
19 | #include <linux/fb.h> | 19 | #include <linux/fb.h> |
20 | #include <linux/backlight.h> | 20 | #include <linux/backlight.h> |
21 | #include <asm/arch/sharpsl.h> | ||
22 | #include <asm/hardware/sharpsl_pm.h> | ||
23 | 21 | ||
24 | static int corgibl_intensity; | 22 | static int corgibl_intensity; |
25 | static struct backlight_properties corgibl_data; | 23 | static struct backlight_properties corgibl_data; |
26 | static struct backlight_device *corgi_backlight_device; | 24 | static struct backlight_device *corgi_backlight_device; |
27 | static struct corgibl_machinfo *bl_machinfo; | 25 | static struct generic_bl_info *bl_machinfo; |
28 | 26 | ||
29 | static unsigned long corgibl_flags; | 27 | static unsigned long corgibl_flags; |
30 | #define CORGIBL_SUSPENDED 0x01 | 28 | #define CORGIBL_SUSPENDED 0x01 |
@@ -32,7 +30,6 @@ static unsigned long corgibl_flags; | |||
32 | 30 | ||
33 | static int corgibl_send_intensity(struct backlight_device *bd) | 31 | static int corgibl_send_intensity(struct backlight_device *bd) |
34 | { | 32 | { |
35 | void (*corgi_kick_batt)(void); | ||
36 | int intensity = bd->props.brightness; | 33 | int intensity = bd->props.brightness; |
37 | 34 | ||
38 | if (bd->props.power != FB_BLANK_UNBLANK) | 35 | if (bd->props.power != FB_BLANK_UNBLANK) |
@@ -48,11 +45,8 @@ static int corgibl_send_intensity(struct backlight_device *bd) | |||
48 | 45 | ||
49 | corgibl_intensity = intensity; | 46 | corgibl_intensity = intensity; |
50 | 47 | ||
51 | corgi_kick_batt = symbol_get(sharpsl_battery_kick); | 48 | if (bl_machinfo->kick_battery) |
52 | if (corgi_kick_batt) { | 49 | bl_machinfo->kick_battery(); |
53 | corgi_kick_batt(); | ||
54 | symbol_put(sharpsl_battery_kick); | ||
55 | } | ||
56 | 50 | ||
57 | return 0; | 51 | return 0; |
58 | } | 52 | } |
@@ -107,13 +101,17 @@ static struct backlight_ops corgibl_ops = { | |||
107 | 101 | ||
108 | static int corgibl_probe(struct platform_device *pdev) | 102 | static int corgibl_probe(struct platform_device *pdev) |
109 | { | 103 | { |
110 | struct corgibl_machinfo *machinfo = pdev->dev.platform_data; | 104 | struct generic_bl_info *machinfo = pdev->dev.platform_data; |
105 | const char *name = "generic-bl"; | ||
111 | 106 | ||
112 | bl_machinfo = machinfo; | 107 | bl_machinfo = machinfo; |
113 | if (!machinfo->limit_mask) | 108 | if (!machinfo->limit_mask) |
114 | machinfo->limit_mask = -1; | 109 | machinfo->limit_mask = -1; |
115 | 110 | ||
116 | corgi_backlight_device = backlight_device_register ("corgi-bl", | 111 | if (machinfo->name) |
112 | name = machinfo->name; | ||
113 | |||
114 | corgi_backlight_device = backlight_device_register (name, | ||
117 | &pdev->dev, NULL, &corgibl_ops); | 115 | &pdev->dev, NULL, &corgibl_ops); |
118 | if (IS_ERR (corgi_backlight_device)) | 116 | if (IS_ERR (corgi_backlight_device)) |
119 | return PTR_ERR (corgi_backlight_device); | 117 | return PTR_ERR (corgi_backlight_device); |
@@ -149,7 +147,7 @@ static struct platform_driver corgibl_driver = { | |||
149 | .suspend = corgibl_suspend, | 147 | .suspend = corgibl_suspend, |
150 | .resume = corgibl_resume, | 148 | .resume = corgibl_resume, |
151 | .driver = { | 149 | .driver = { |
152 | .name = "corgi-bl", | 150 | .name = "generic-bl", |
153 | }, | 151 | }, |
154 | }; | 152 | }; |
155 | 153 | ||
diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c index b7904da51b23..92e201e81fbd 100644 --- a/drivers/video/backlight/cr_bllcd.c +++ b/drivers/video/backlight/cr_bllcd.c | |||
@@ -171,13 +171,11 @@ static struct lcd_ops cr_lcd_ops = { | |||
171 | 171 | ||
172 | static int cr_backlight_probe(struct platform_device *pdev) | 172 | static int cr_backlight_probe(struct platform_device *pdev) |
173 | { | 173 | { |
174 | struct backlight_device *bdp; | ||
175 | struct lcd_device *ldp; | ||
174 | struct cr_panel *crp; | 176 | struct cr_panel *crp; |
175 | u8 dev_en; | 177 | u8 dev_en; |
176 | 178 | ||
177 | crp = kzalloc(sizeof(*crp), GFP_KERNEL); | ||
178 | if (crp == NULL) | ||
179 | return -ENOMEM; | ||
180 | |||
181 | lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL, | 179 | lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL, |
182 | CRVML_DEVICE_LPC, NULL); | 180 | CRVML_DEVICE_LPC, NULL); |
183 | if (!lpc_dev) { | 181 | if (!lpc_dev) { |
@@ -193,27 +191,34 @@ static int cr_backlight_probe(struct platform_device *pdev) | |||
193 | return -ENODEV; | 191 | return -ENODEV; |
194 | } | 192 | } |
195 | 193 | ||
196 | crp->cr_backlight_device = backlight_device_register("cr-backlight", | 194 | bdp = backlight_device_register("cr-backlight", |
197 | &pdev->dev, NULL, | 195 | &pdev->dev, NULL, &cr_backlight_ops); |
198 | &cr_backlight_ops); | 196 | if (IS_ERR(bdp)) { |
199 | if (IS_ERR(crp->cr_backlight_device)) { | ||
200 | pci_dev_put(lpc_dev); | 197 | pci_dev_put(lpc_dev); |
201 | return PTR_ERR(crp->cr_backlight_device); | 198 | return PTR_ERR(bdp); |
202 | } | 199 | } |
203 | 200 | ||
204 | crp->cr_lcd_device = lcd_device_register("cr-lcd", | 201 | ldp = lcd_device_register("cr-lcd", &pdev->dev, NULL, &cr_lcd_ops); |
205 | &pdev->dev, NULL, | 202 | if (IS_ERR(ldp)) { |
206 | &cr_lcd_ops); | 203 | backlight_device_unregister(bdp); |
207 | |||
208 | if (IS_ERR(crp->cr_lcd_device)) { | ||
209 | pci_dev_put(lpc_dev); | 204 | pci_dev_put(lpc_dev); |
210 | return PTR_ERR(crp->cr_backlight_device); | 205 | return PTR_ERR(bdp); |
211 | } | 206 | } |
212 | 207 | ||
213 | pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR, | 208 | pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR, |
214 | &gpio_bar); | 209 | &gpio_bar); |
215 | gpio_bar &= ~0x3F; | 210 | gpio_bar &= ~0x3F; |
216 | 211 | ||
212 | crp = kzalloc(sizeof(*crp), GFP_KERNEL); | ||
213 | if (!crp) { | ||
214 | lcd_device_unregister(ldp); | ||
215 | backlight_device_unregister(bdp); | ||
216 | pci_dev_put(lpc_dev); | ||
217 | return -ENOMEM; | ||
218 | } | ||
219 | |||
220 | crp->cr_backlight_device = bdp; | ||
221 | crp->cr_lcd_device = ldp; | ||
217 | crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK; | 222 | crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK; |
218 | crp->cr_backlight_device->props.brightness = 0; | 223 | crp->cr_backlight_device->props.brightness = 0; |
219 | crp->cr_backlight_device->props.max_brightness = 0; | 224 | crp->cr_backlight_device->props.max_brightness = 0; |
diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 6f652c65fae1..299fd318dd45 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c | |||
@@ -149,7 +149,7 @@ static ssize_t lcd_show_max_contrast(struct device *dev, | |||
149 | return sprintf(buf, "%d\n", ld->props.max_contrast); | 149 | return sprintf(buf, "%d\n", ld->props.max_contrast); |
150 | } | 150 | } |
151 | 151 | ||
152 | struct class *lcd_class; | 152 | static struct class *lcd_class; |
153 | 153 | ||
154 | static void lcd_device_release(struct device *dev) | 154 | static void lcd_device_release(struct device *dev) |
155 | { | 155 | { |
diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c new file mode 100644 index 000000000000..2eb206bf73e6 --- /dev/null +++ b/drivers/video/backlight/ltv350qv.c | |||
@@ -0,0 +1,330 @@ | |||
1 | /* | ||
2 | * Power control for Samsung LTV350QV Quarter VGA LCD Panel | ||
3 | * | ||
4 | * Copyright (C) 2006, 2007 Atmel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/delay.h> | ||
11 | #include <linux/err.h> | ||
12 | #include <linux/fb.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/lcd.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/spi/spi.h> | ||
17 | |||
18 | #include "ltv350qv.h" | ||
19 | |||
20 | #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) | ||
21 | |||
22 | struct ltv350qv { | ||
23 | struct spi_device *spi; | ||
24 | u8 *buffer; | ||
25 | int power; | ||
26 | struct lcd_device *ld; | ||
27 | }; | ||
28 | |||
29 | /* | ||
30 | * The power-on and power-off sequences are taken from the | ||
31 | * LTV350QV-F04 data sheet from Samsung. The register definitions are | ||
32 | * taken from the S6F2002 command list also from Samsung. Both | ||
33 | * documents are distributed with the AVR32 Linux BSP CD from Atmel. | ||
34 | * | ||
35 | * There's still some voodoo going on here, but it's a lot better than | ||
36 | * in the first incarnation of the driver where all we had was the raw | ||
37 | * numbers from the initialization sequence. | ||
38 | */ | ||
39 | static int ltv350qv_write_reg(struct ltv350qv *lcd, u8 reg, u16 val) | ||
40 | { | ||
41 | struct spi_message msg; | ||
42 | struct spi_transfer index_xfer = { | ||
43 | .len = 3, | ||
44 | .cs_change = 1, | ||
45 | }; | ||
46 | struct spi_transfer value_xfer = { | ||
47 | .len = 3, | ||
48 | }; | ||
49 | |||
50 | spi_message_init(&msg); | ||
51 | |||
52 | /* register index */ | ||
53 | lcd->buffer[0] = LTV_OPC_INDEX; | ||
54 | lcd->buffer[1] = 0x00; | ||
55 | lcd->buffer[2] = reg & 0x7f; | ||
56 | index_xfer.tx_buf = lcd->buffer; | ||
57 | spi_message_add_tail(&index_xfer, &msg); | ||
58 | |||
59 | /* register value */ | ||
60 | lcd->buffer[4] = LTV_OPC_DATA; | ||
61 | lcd->buffer[5] = val >> 8; | ||
62 | lcd->buffer[6] = val; | ||
63 | value_xfer.tx_buf = lcd->buffer + 4; | ||
64 | spi_message_add_tail(&value_xfer, &msg); | ||
65 | |||
66 | return spi_sync(lcd->spi, &msg); | ||
67 | } | ||
68 | |||
69 | /* The comments are taken straight from the data sheet */ | ||
70 | static int ltv350qv_power_on(struct ltv350qv *lcd) | ||
71 | { | ||
72 | int ret; | ||
73 | |||
74 | /* Power On Reset Display off State */ | ||
75 | if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, 0x0000)) | ||
76 | goto err; | ||
77 | msleep(15); | ||
78 | |||
79 | /* Power Setting Function 1 */ | ||
80 | if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE)) | ||
81 | goto err; | ||
82 | if (ltv350qv_write_reg(lcd, LTV_PWRCTL2, LTV_VCOML_ENABLE)) | ||
83 | goto err_power1; | ||
84 | |||
85 | /* Power Setting Function 2 */ | ||
86 | if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, | ||
87 | LTV_VCOM_DISABLE | LTV_DRIVE_CURRENT(5) | ||
88 | | LTV_SUPPLY_CURRENT(5))) | ||
89 | goto err_power2; | ||
90 | |||
91 | msleep(55); | ||
92 | |||
93 | /* Instruction Setting */ | ||
94 | ret = ltv350qv_write_reg(lcd, LTV_IFCTL, | ||
95 | LTV_NMD | LTV_REV | LTV_NL(0x1d)); | ||
96 | ret |= ltv350qv_write_reg(lcd, LTV_DATACTL, | ||
97 | LTV_DS_SAME | LTV_CHS_480 | ||
98 | | LTV_DF_RGB | LTV_RGB_BGR); | ||
99 | ret |= ltv350qv_write_reg(lcd, LTV_ENTRY_MODE, | ||
100 | LTV_VSPL_ACTIVE_LOW | ||
101 | | LTV_HSPL_ACTIVE_LOW | ||
102 | | LTV_DPL_SAMPLE_RISING | ||
103 | | LTV_EPL_ACTIVE_LOW | ||
104 | | LTV_SS_RIGHT_TO_LEFT); | ||
105 | ret |= ltv350qv_write_reg(lcd, LTV_GATECTL1, LTV_CLW(3)); | ||
106 | ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2, | ||
107 | LTV_NW_INV_1LINE | LTV_FWI(3)); | ||
108 | ret |= ltv350qv_write_reg(lcd, LTV_VBP, 0x000a); | ||
109 | ret |= ltv350qv_write_reg(lcd, LTV_HBP, 0x0021); | ||
110 | ret |= ltv350qv_write_reg(lcd, LTV_SOTCTL, LTV_SDT(3) | LTV_EQ(0)); | ||
111 | ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(0), 0x0103); | ||
112 | ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(1), 0x0301); | ||
113 | ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(2), 0x1f0f); | ||
114 | ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(3), 0x1f0f); | ||
115 | ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(4), 0x0707); | ||
116 | ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(5), 0x0307); | ||
117 | ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(6), 0x0707); | ||
118 | ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(7), 0x0000); | ||
119 | ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(8), 0x0004); | ||
120 | ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(9), 0x0000); | ||
121 | if (ret) | ||
122 | goto err_settings; | ||
123 | |||
124 | /* Wait more than 2 frames */ | ||
125 | msleep(20); | ||
126 | |||
127 | /* Display On Sequence */ | ||
128 | ret = ltv350qv_write_reg(lcd, LTV_PWRCTL1, | ||
129 | LTV_VCOM_DISABLE | LTV_VCOMOUT_ENABLE | ||
130 | | LTV_POWER_ON | LTV_DRIVE_CURRENT(5) | ||
131 | | LTV_SUPPLY_CURRENT(5)); | ||
132 | ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2, | ||
133 | LTV_NW_INV_1LINE | LTV_DSC | LTV_FWI(3)); | ||
134 | if (ret) | ||
135 | goto err_disp_on; | ||
136 | |||
137 | /* Display should now be ON. Phew. */ | ||
138 | return 0; | ||
139 | |||
140 | err_disp_on: | ||
141 | /* | ||
142 | * Try to recover. Error handling probably isn't very useful | ||
143 | * at this point, just make a best effort to switch the panel | ||
144 | * off. | ||
145 | */ | ||
146 | ltv350qv_write_reg(lcd, LTV_PWRCTL1, | ||
147 | LTV_VCOM_DISABLE | LTV_DRIVE_CURRENT(5) | ||
148 | | LTV_SUPPLY_CURRENT(5)); | ||
149 | ltv350qv_write_reg(lcd, LTV_GATECTL2, | ||
150 | LTV_NW_INV_1LINE | LTV_FWI(3)); | ||
151 | err_settings: | ||
152 | err_power2: | ||
153 | err_power1: | ||
154 | ltv350qv_write_reg(lcd, LTV_PWRCTL2, 0x0000); | ||
155 | msleep(1); | ||
156 | err: | ||
157 | ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE); | ||
158 | return -EIO; | ||
159 | } | ||
160 | |||
161 | static int ltv350qv_power_off(struct ltv350qv *lcd) | ||
162 | { | ||
163 | int ret; | ||
164 | |||
165 | /* Display Off Sequence */ | ||
166 | ret = ltv350qv_write_reg(lcd, LTV_PWRCTL1, | ||
167 | LTV_VCOM_DISABLE | ||
168 | | LTV_DRIVE_CURRENT(5) | ||
169 | | LTV_SUPPLY_CURRENT(5)); | ||
170 | ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2, | ||
171 | LTV_NW_INV_1LINE | LTV_FWI(3)); | ||
172 | |||
173 | /* Power down setting 1 */ | ||
174 | ret |= ltv350qv_write_reg(lcd, LTV_PWRCTL2, 0x0000); | ||
175 | |||
176 | /* Wait at least 1 ms */ | ||
177 | msleep(1); | ||
178 | |||
179 | /* Power down setting 2 */ | ||
180 | ret |= ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE); | ||
181 | |||
182 | /* | ||
183 | * No point in trying to recover here. If we can't switch the | ||
184 | * panel off, what are we supposed to do other than inform the | ||
185 | * user about the failure? | ||
186 | */ | ||
187 | if (ret) | ||
188 | return -EIO; | ||
189 | |||
190 | /* Display power should now be OFF */ | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int ltv350qv_power(struct ltv350qv *lcd, int power) | ||
195 | { | ||
196 | int ret = 0; | ||
197 | |||
198 | if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) | ||
199 | ret = ltv350qv_power_on(lcd); | ||
200 | else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) | ||
201 | ret = ltv350qv_power_off(lcd); | ||
202 | |||
203 | if (!ret) | ||
204 | lcd->power = power; | ||
205 | |||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | static int ltv350qv_set_power(struct lcd_device *ld, int power) | ||
210 | { | ||
211 | struct ltv350qv *lcd = lcd_get_data(ld); | ||
212 | |||
213 | return ltv350qv_power(lcd, power); | ||
214 | } | ||
215 | |||
216 | static int ltv350qv_get_power(struct lcd_device *ld) | ||
217 | { | ||
218 | struct ltv350qv *lcd = lcd_get_data(ld); | ||
219 | |||
220 | return lcd->power; | ||
221 | } | ||
222 | |||
223 | static struct lcd_ops ltv_ops = { | ||
224 | .get_power = ltv350qv_get_power, | ||
225 | .set_power = ltv350qv_set_power, | ||
226 | }; | ||
227 | |||
228 | static int __devinit ltv350qv_probe(struct spi_device *spi) | ||
229 | { | ||
230 | struct ltv350qv *lcd; | ||
231 | struct lcd_device *ld; | ||
232 | int ret; | ||
233 | |||
234 | lcd = kzalloc(sizeof(struct ltv350qv), GFP_KERNEL); | ||
235 | if (!lcd) | ||
236 | return -ENOMEM; | ||
237 | |||
238 | lcd->spi = spi; | ||
239 | lcd->power = FB_BLANK_POWERDOWN; | ||
240 | lcd->buffer = kzalloc(8, GFP_KERNEL); | ||
241 | |||
242 | ld = lcd_device_register("ltv350qv", &spi->dev, lcd, <v_ops); | ||
243 | if (IS_ERR(ld)) { | ||
244 | ret = PTR_ERR(ld); | ||
245 | goto out_free_lcd; | ||
246 | } | ||
247 | lcd->ld = ld; | ||
248 | |||
249 | ret = ltv350qv_power(lcd, FB_BLANK_UNBLANK); | ||
250 | if (ret) | ||
251 | goto out_unregister; | ||
252 | |||
253 | dev_set_drvdata(&spi->dev, lcd); | ||
254 | |||
255 | return 0; | ||
256 | |||
257 | out_unregister: | ||
258 | lcd_device_unregister(ld); | ||
259 | out_free_lcd: | ||
260 | kfree(lcd); | ||
261 | return ret; | ||
262 | } | ||
263 | |||
264 | static int __devexit ltv350qv_remove(struct spi_device *spi) | ||
265 | { | ||
266 | struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); | ||
267 | |||
268 | ltv350qv_power(lcd, FB_BLANK_POWERDOWN); | ||
269 | lcd_device_unregister(lcd->ld); | ||
270 | kfree(lcd); | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | #ifdef CONFIG_PM | ||
276 | static int ltv350qv_suspend(struct spi_device *spi, pm_message_t state) | ||
277 | { | ||
278 | struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); | ||
279 | |||
280 | return ltv350qv_power(lcd, FB_BLANK_POWERDOWN); | ||
281 | } | ||
282 | |||
283 | static int ltv350qv_resume(struct spi_device *spi) | ||
284 | { | ||
285 | struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); | ||
286 | |||
287 | return ltv350qv_power(lcd, FB_BLANK_UNBLANK); | ||
288 | } | ||
289 | #else | ||
290 | #define ltv350qv_suspend NULL | ||
291 | #define ltv350qv_resume NULL | ||
292 | #endif | ||
293 | |||
294 | /* Power down all displays on reboot, poweroff or halt */ | ||
295 | static void ltv350qv_shutdown(struct spi_device *spi) | ||
296 | { | ||
297 | struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); | ||
298 | |||
299 | ltv350qv_power(lcd, FB_BLANK_POWERDOWN); | ||
300 | } | ||
301 | |||
302 | static struct spi_driver ltv350qv_driver = { | ||
303 | .driver = { | ||
304 | .name = "ltv350qv", | ||
305 | .bus = &spi_bus_type, | ||
306 | .owner = THIS_MODULE, | ||
307 | }, | ||
308 | |||
309 | .probe = ltv350qv_probe, | ||
310 | .remove = __devexit_p(ltv350qv_remove), | ||
311 | .shutdown = ltv350qv_shutdown, | ||
312 | .suspend = ltv350qv_suspend, | ||
313 | .resume = ltv350qv_resume, | ||
314 | }; | ||
315 | |||
316 | static int __init ltv350qv_init(void) | ||
317 | { | ||
318 | return spi_register_driver(<v350qv_driver); | ||
319 | } | ||
320 | |||
321 | static void __exit ltv350qv_exit(void) | ||
322 | { | ||
323 | spi_unregister_driver(<v350qv_driver); | ||
324 | } | ||
325 | module_init(ltv350qv_init); | ||
326 | module_exit(ltv350qv_exit); | ||
327 | |||
328 | MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); | ||
329 | MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver"); | ||
330 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/backlight/ltv350qv.h b/drivers/video/backlight/ltv350qv.h new file mode 100644 index 000000000000..189112e3fc7a --- /dev/null +++ b/drivers/video/backlight/ltv350qv.h | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * Register definitions for Samsung LTV350QV Quarter VGA LCD Panel | ||
3 | * | ||
4 | * Copyright (C) 2006, 2007 Atmel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #ifndef __LTV350QV_H | ||
11 | #define __LTV350QV_H | ||
12 | |||
13 | #define LTV_OPC_INDEX 0x74 | ||
14 | #define LTV_OPC_DATA 0x76 | ||
15 | |||
16 | #define LTV_ID 0x00 /* ID Read */ | ||
17 | #define LTV_IFCTL 0x01 /* Display Interface Control */ | ||
18 | #define LTV_DATACTL 0x02 /* Display Data Control */ | ||
19 | #define LTV_ENTRY_MODE 0x03 /* Entry Mode */ | ||
20 | #define LTV_GATECTL1 0x04 /* Gate Control 1 */ | ||
21 | #define LTV_GATECTL2 0x05 /* Gate Control 2 */ | ||
22 | #define LTV_VBP 0x06 /* Vertical Back Porch */ | ||
23 | #define LTV_HBP 0x07 /* Horizontal Back Porch */ | ||
24 | #define LTV_SOTCTL 0x08 /* Source Output Timing Control */ | ||
25 | #define LTV_PWRCTL1 0x09 /* Power Control 1 */ | ||
26 | #define LTV_PWRCTL2 0x0a /* Power Control 2 */ | ||
27 | #define LTV_GAMMA(x) (0x10 + (x)) /* Gamma control */ | ||
28 | |||
29 | /* Bit definitions for LTV_IFCTL */ | ||
30 | #define LTV_IM (1 << 15) | ||
31 | #define LTV_NMD (1 << 14) | ||
32 | #define LTV_SSMD (1 << 13) | ||
33 | #define LTV_REV (1 << 7) | ||
34 | #define LTV_NL(x) (((x) & 0x001f) << 0) | ||
35 | |||
36 | /* Bit definitions for LTV_DATACTL */ | ||
37 | #define LTV_DS_SAME (0 << 12) | ||
38 | #define LTV_DS_D_TO_S (1 << 12) | ||
39 | #define LTV_DS_S_TO_D (2 << 12) | ||
40 | #define LTV_CHS_384 (0 << 9) | ||
41 | #define LTV_CHS_480 (1 << 9) | ||
42 | #define LTV_CHS_492 (2 << 9) | ||
43 | #define LTV_DF_RGB (0 << 6) | ||
44 | #define LTV_DF_RGBX (1 << 6) | ||
45 | #define LTV_DF_XRGB (2 << 6) | ||
46 | #define LTV_RGB_RGB (0 << 2) | ||
47 | #define LTV_RGB_BGR (1 << 2) | ||
48 | #define LTV_RGB_GRB (2 << 2) | ||
49 | #define LTV_RGB_RBG (3 << 2) | ||
50 | |||
51 | /* Bit definitions for LTV_ENTRY_MODE */ | ||
52 | #define LTV_VSPL_ACTIVE_LOW (0 << 15) | ||
53 | #define LTV_VSPL_ACTIVE_HIGH (1 << 15) | ||
54 | #define LTV_HSPL_ACTIVE_LOW (0 << 14) | ||
55 | #define LTV_HSPL_ACTIVE_HIGH (1 << 14) | ||
56 | #define LTV_DPL_SAMPLE_RISING (0 << 13) | ||
57 | #define LTV_DPL_SAMPLE_FALLING (1 << 13) | ||
58 | #define LTV_EPL_ACTIVE_LOW (0 << 12) | ||
59 | #define LTV_EPL_ACTIVE_HIGH (1 << 12) | ||
60 | #define LTV_SS_LEFT_TO_RIGHT (0 << 8) | ||
61 | #define LTV_SS_RIGHT_TO_LEFT (1 << 8) | ||
62 | #define LTV_STB (1 << 1) | ||
63 | |||
64 | /* Bit definitions for LTV_GATECTL1 */ | ||
65 | #define LTV_CLW(x) (((x) & 0x0007) << 12) | ||
66 | #define LTV_GAON (1 << 5) | ||
67 | #define LTV_SDR (1 << 3) | ||
68 | |||
69 | /* Bit definitions for LTV_GATECTL2 */ | ||
70 | #define LTV_NW_INV_FRAME (0 << 14) | ||
71 | #define LTV_NW_INV_1LINE (1 << 14) | ||
72 | #define LTV_NW_INV_2LINE (2 << 14) | ||
73 | #define LTV_DSC (1 << 12) | ||
74 | #define LTV_GIF (1 << 8) | ||
75 | #define LTV_FHN (1 << 7) | ||
76 | #define LTV_FTI(x) (((x) & 0x0003) << 4) | ||
77 | #define LTV_FWI(x) (((x) & 0x0003) << 0) | ||
78 | |||
79 | /* Bit definitions for LTV_SOTCTL */ | ||
80 | #define LTV_SDT(x) (((x) & 0x0007) << 10) | ||
81 | #define LTV_EQ(x) (((x) & 0x0007) << 2) | ||
82 | |||
83 | /* Bit definitions for LTV_PWRCTL1 */ | ||
84 | #define LTV_VCOM_DISABLE (1 << 14) | ||
85 | #define LTV_VCOMOUT_ENABLE (1 << 11) | ||
86 | #define LTV_POWER_ON (1 << 9) | ||
87 | #define LTV_DRIVE_CURRENT(x) (((x) & 0x0007) << 4) /* 0=off, 5=max */ | ||
88 | #define LTV_SUPPLY_CURRENT(x) (((x) & 0x0007) << 0) /* 0=off, 5=max */ | ||
89 | |||
90 | /* Bit definitions for LTV_PWRCTL2 */ | ||
91 | #define LTV_VCOML_ENABLE (1 << 13) | ||
92 | #define LTV_VCOML_VOLTAGE(x) (((x) & 0x001f) << 8) /* 0=1V, 31=-1V */ | ||
93 | #define LTV_VCOMH_VOLTAGE(x) (((x) & 0x001f) << 0) /* 0=3V, 31=4.5V */ | ||
94 | |||
95 | #endif /* __LTV350QV_H */ | ||