Commit dd37739f47ea278a57d66b2afe20243f0a6294a0
Committed by
Paul Mundt
1 parent
20733d59d5
Exists in
master
and in
6 other branches
Remove unneeded version.h includes from drivers/video/
It was pointed out by 'make versioncheck' that some includes of linux/version.h are not needed in drivers/video/. This patch removes them. Signed-off-by: Jesper Juhl <jj@chaosbits.net> Acked-by: Mike Frysinger <vapier@gentoo.org> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Showing 4 changed files with 0 additions and 5 deletions Inline Diff
drivers/video/backlight/adp8860_bl.c
1 | /* | 1 | /* |
2 | * Backlight driver for Analog Devices ADP8860 Backlight Devices | 2 | * Backlight driver for Analog Devices ADP8860 Backlight Devices |
3 | * | 3 | * |
4 | * Copyright 2009-2010 Analog Devices Inc. | 4 | * Copyright 2009-2010 Analog Devices Inc. |
5 | * | 5 | * |
6 | * Licensed under the GPL-2 or later. | 6 | * Licensed under the GPL-2 or later. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
10 | #include <linux/version.h> | ||
11 | #include <linux/init.h> | 10 | #include <linux/init.h> |
12 | #include <linux/errno.h> | 11 | #include <linux/errno.h> |
13 | #include <linux/pm.h> | 12 | #include <linux/pm.h> |
14 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
15 | #include <linux/i2c.h> | 14 | #include <linux/i2c.h> |
16 | #include <linux/fb.h> | 15 | #include <linux/fb.h> |
17 | #include <linux/backlight.h> | 16 | #include <linux/backlight.h> |
18 | #include <linux/leds.h> | 17 | #include <linux/leds.h> |
19 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
20 | #include <linux/workqueue.h> | 19 | #include <linux/workqueue.h> |
21 | 20 | ||
22 | #include <linux/i2c/adp8860.h> | 21 | #include <linux/i2c/adp8860.h> |
23 | #define ADP8860_EXT_FEATURES | 22 | #define ADP8860_EXT_FEATURES |
24 | #define ADP8860_USE_LEDS | 23 | #define ADP8860_USE_LEDS |
25 | 24 | ||
26 | #define ADP8860_MFDVID 0x00 /* Manufacturer and device ID */ | 25 | #define ADP8860_MFDVID 0x00 /* Manufacturer and device ID */ |
27 | #define ADP8860_MDCR 0x01 /* Device mode and status */ | 26 | #define ADP8860_MDCR 0x01 /* Device mode and status */ |
28 | #define ADP8860_MDCR2 0x02 /* Device mode and Status Register 2 */ | 27 | #define ADP8860_MDCR2 0x02 /* Device mode and Status Register 2 */ |
29 | #define ADP8860_INTR_EN 0x03 /* Interrupts enable */ | 28 | #define ADP8860_INTR_EN 0x03 /* Interrupts enable */ |
30 | #define ADP8860_CFGR 0x04 /* Configuration register */ | 29 | #define ADP8860_CFGR 0x04 /* Configuration register */ |
31 | #define ADP8860_BLSEN 0x05 /* Sink enable backlight or independent */ | 30 | #define ADP8860_BLSEN 0x05 /* Sink enable backlight or independent */ |
32 | #define ADP8860_BLOFF 0x06 /* Backlight off timeout */ | 31 | #define ADP8860_BLOFF 0x06 /* Backlight off timeout */ |
33 | #define ADP8860_BLDIM 0x07 /* Backlight dim timeout */ | 32 | #define ADP8860_BLDIM 0x07 /* Backlight dim timeout */ |
34 | #define ADP8860_BLFR 0x08 /* Backlight fade in and out rates */ | 33 | #define ADP8860_BLFR 0x08 /* Backlight fade in and out rates */ |
35 | #define ADP8860_BLMX1 0x09 /* Backlight (Brightness Level 1-daylight) maximum current */ | 34 | #define ADP8860_BLMX1 0x09 /* Backlight (Brightness Level 1-daylight) maximum current */ |
36 | #define ADP8860_BLDM1 0x0A /* Backlight (Brightness Level 1-daylight) dim current */ | 35 | #define ADP8860_BLDM1 0x0A /* Backlight (Brightness Level 1-daylight) dim current */ |
37 | #define ADP8860_BLMX2 0x0B /* Backlight (Brightness Level 2-office) maximum current */ | 36 | #define ADP8860_BLMX2 0x0B /* Backlight (Brightness Level 2-office) maximum current */ |
38 | #define ADP8860_BLDM2 0x0C /* Backlight (Brightness Level 2-office) dim current */ | 37 | #define ADP8860_BLDM2 0x0C /* Backlight (Brightness Level 2-office) dim current */ |
39 | #define ADP8860_BLMX3 0x0D /* Backlight (Brightness Level 3-dark) maximum current */ | 38 | #define ADP8860_BLMX3 0x0D /* Backlight (Brightness Level 3-dark) maximum current */ |
40 | #define ADP8860_BLDM3 0x0E /* Backlight (Brightness Level 3-dark) dim current */ | 39 | #define ADP8860_BLDM3 0x0E /* Backlight (Brightness Level 3-dark) dim current */ |
41 | #define ADP8860_ISCFR 0x0F /* Independent sink current fade control register */ | 40 | #define ADP8860_ISCFR 0x0F /* Independent sink current fade control register */ |
42 | #define ADP8860_ISCC 0x10 /* Independent sink current control register */ | 41 | #define ADP8860_ISCC 0x10 /* Independent sink current control register */ |
43 | #define ADP8860_ISCT1 0x11 /* Independent Sink Current Timer Register LED[7:5] */ | 42 | #define ADP8860_ISCT1 0x11 /* Independent Sink Current Timer Register LED[7:5] */ |
44 | #define ADP8860_ISCT2 0x12 /* Independent Sink Current Timer Register LED[4:1] */ | 43 | #define ADP8860_ISCT2 0x12 /* Independent Sink Current Timer Register LED[4:1] */ |
45 | #define ADP8860_ISCF 0x13 /* Independent sink current fade register */ | 44 | #define ADP8860_ISCF 0x13 /* Independent sink current fade register */ |
46 | #define ADP8860_ISC7 0x14 /* Independent Sink Current LED7 */ | 45 | #define ADP8860_ISC7 0x14 /* Independent Sink Current LED7 */ |
47 | #define ADP8860_ISC6 0x15 /* Independent Sink Current LED6 */ | 46 | #define ADP8860_ISC6 0x15 /* Independent Sink Current LED6 */ |
48 | #define ADP8860_ISC5 0x16 /* Independent Sink Current LED5 */ | 47 | #define ADP8860_ISC5 0x16 /* Independent Sink Current LED5 */ |
49 | #define ADP8860_ISC4 0x17 /* Independent Sink Current LED4 */ | 48 | #define ADP8860_ISC4 0x17 /* Independent Sink Current LED4 */ |
50 | #define ADP8860_ISC3 0x18 /* Independent Sink Current LED3 */ | 49 | #define ADP8860_ISC3 0x18 /* Independent Sink Current LED3 */ |
51 | #define ADP8860_ISC2 0x19 /* Independent Sink Current LED2 */ | 50 | #define ADP8860_ISC2 0x19 /* Independent Sink Current LED2 */ |
52 | #define ADP8860_ISC1 0x1A /* Independent Sink Current LED1 */ | 51 | #define ADP8860_ISC1 0x1A /* Independent Sink Current LED1 */ |
53 | #define ADP8860_CCFG 0x1B /* Comparator configuration */ | 52 | #define ADP8860_CCFG 0x1B /* Comparator configuration */ |
54 | #define ADP8860_CCFG2 0x1C /* Second comparator configuration */ | 53 | #define ADP8860_CCFG2 0x1C /* Second comparator configuration */ |
55 | #define ADP8860_L2_TRP 0x1D /* L2 comparator reference */ | 54 | #define ADP8860_L2_TRP 0x1D /* L2 comparator reference */ |
56 | #define ADP8860_L2_HYS 0x1E /* L2 hysteresis */ | 55 | #define ADP8860_L2_HYS 0x1E /* L2 hysteresis */ |
57 | #define ADP8860_L3_TRP 0x1F /* L3 comparator reference */ | 56 | #define ADP8860_L3_TRP 0x1F /* L3 comparator reference */ |
58 | #define ADP8860_L3_HYS 0x20 /* L3 hysteresis */ | 57 | #define ADP8860_L3_HYS 0x20 /* L3 hysteresis */ |
59 | #define ADP8860_PH1LEVL 0x21 /* First phototransistor ambient light level-low byte register */ | 58 | #define ADP8860_PH1LEVL 0x21 /* First phototransistor ambient light level-low byte register */ |
60 | #define ADP8860_PH1LEVH 0x22 /* First phototransistor ambient light level-high byte register */ | 59 | #define ADP8860_PH1LEVH 0x22 /* First phototransistor ambient light level-high byte register */ |
61 | #define ADP8860_PH2LEVL 0x23 /* Second phototransistor ambient light level-low byte register */ | 60 | #define ADP8860_PH2LEVL 0x23 /* Second phototransistor ambient light level-low byte register */ |
62 | #define ADP8860_PH2LEVH 0x24 /* Second phototransistor ambient light level-high byte register */ | 61 | #define ADP8860_PH2LEVH 0x24 /* Second phototransistor ambient light level-high byte register */ |
63 | 62 | ||
64 | #define ADP8860_MANUFID 0x0 /* Analog Devices ADP8860 Manufacturer ID */ | 63 | #define ADP8860_MANUFID 0x0 /* Analog Devices ADP8860 Manufacturer ID */ |
65 | #define ADP8861_MANUFID 0x4 /* Analog Devices ADP8861 Manufacturer ID */ | 64 | #define ADP8861_MANUFID 0x4 /* Analog Devices ADP8861 Manufacturer ID */ |
66 | #define ADP8863_MANUFID 0x2 /* Analog Devices ADP8863 Manufacturer ID */ | 65 | #define ADP8863_MANUFID 0x2 /* Analog Devices ADP8863 Manufacturer ID */ |
67 | 66 | ||
68 | #define ADP8860_DEVID(x) ((x) & 0xF) | 67 | #define ADP8860_DEVID(x) ((x) & 0xF) |
69 | #define ADP8860_MANID(x) ((x) >> 4) | 68 | #define ADP8860_MANID(x) ((x) >> 4) |
70 | 69 | ||
71 | /* MDCR Device mode and status */ | 70 | /* MDCR Device mode and status */ |
72 | #define INT_CFG (1 << 6) | 71 | #define INT_CFG (1 << 6) |
73 | #define NSTBY (1 << 5) | 72 | #define NSTBY (1 << 5) |
74 | #define DIM_EN (1 << 4) | 73 | #define DIM_EN (1 << 4) |
75 | #define GDWN_DIS (1 << 3) | 74 | #define GDWN_DIS (1 << 3) |
76 | #define SIS_EN (1 << 2) | 75 | #define SIS_EN (1 << 2) |
77 | #define CMP_AUTOEN (1 << 1) | 76 | #define CMP_AUTOEN (1 << 1) |
78 | #define BLEN (1 << 0) | 77 | #define BLEN (1 << 0) |
79 | 78 | ||
80 | /* ADP8860_CCFG Main ALS comparator level enable */ | 79 | /* ADP8860_CCFG Main ALS comparator level enable */ |
81 | #define L3_EN (1 << 1) | 80 | #define L3_EN (1 << 1) |
82 | #define L2_EN (1 << 0) | 81 | #define L2_EN (1 << 0) |
83 | 82 | ||
84 | #define CFGR_BLV_SHIFT 3 | 83 | #define CFGR_BLV_SHIFT 3 |
85 | #define CFGR_BLV_MASK 0x3 | 84 | #define CFGR_BLV_MASK 0x3 |
86 | #define ADP8860_FLAG_LED_MASK 0xFF | 85 | #define ADP8860_FLAG_LED_MASK 0xFF |
87 | 86 | ||
88 | #define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4)) | 87 | #define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4)) |
89 | #define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1)) | 88 | #define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1)) |
90 | #define ALS_CCFG_VAL(filt) ((0x7 & filt) << 5) | 89 | #define ALS_CCFG_VAL(filt) ((0x7 & filt) << 5) |
91 | 90 | ||
92 | enum { | 91 | enum { |
93 | adp8860, | 92 | adp8860, |
94 | adp8861, | 93 | adp8861, |
95 | adp8863 | 94 | adp8863 |
96 | }; | 95 | }; |
97 | 96 | ||
98 | struct adp8860_led { | 97 | struct adp8860_led { |
99 | struct led_classdev cdev; | 98 | struct led_classdev cdev; |
100 | struct work_struct work; | 99 | struct work_struct work; |
101 | struct i2c_client *client; | 100 | struct i2c_client *client; |
102 | enum led_brightness new_brightness; | 101 | enum led_brightness new_brightness; |
103 | int id; | 102 | int id; |
104 | int flags; | 103 | int flags; |
105 | }; | 104 | }; |
106 | 105 | ||
107 | struct adp8860_bl { | 106 | struct adp8860_bl { |
108 | struct i2c_client *client; | 107 | struct i2c_client *client; |
109 | struct backlight_device *bl; | 108 | struct backlight_device *bl; |
110 | struct adp8860_led *led; | 109 | struct adp8860_led *led; |
111 | struct adp8860_backlight_platform_data *pdata; | 110 | struct adp8860_backlight_platform_data *pdata; |
112 | struct mutex lock; | 111 | struct mutex lock; |
113 | unsigned long cached_daylight_max; | 112 | unsigned long cached_daylight_max; |
114 | int id; | 113 | int id; |
115 | int revid; | 114 | int revid; |
116 | int current_brightness; | 115 | int current_brightness; |
117 | unsigned en_ambl_sens:1; | 116 | unsigned en_ambl_sens:1; |
118 | unsigned gdwn_dis:1; | 117 | unsigned gdwn_dis:1; |
119 | }; | 118 | }; |
120 | 119 | ||
121 | static int adp8860_read(struct i2c_client *client, int reg, uint8_t *val) | 120 | static int adp8860_read(struct i2c_client *client, int reg, uint8_t *val) |
122 | { | 121 | { |
123 | int ret; | 122 | int ret; |
124 | 123 | ||
125 | ret = i2c_smbus_read_byte_data(client, reg); | 124 | ret = i2c_smbus_read_byte_data(client, reg); |
126 | if (ret < 0) { | 125 | if (ret < 0) { |
127 | dev_err(&client->dev, "failed reading at 0x%02x\n", reg); | 126 | dev_err(&client->dev, "failed reading at 0x%02x\n", reg); |
128 | return ret; | 127 | return ret; |
129 | } | 128 | } |
130 | 129 | ||
131 | *val = (uint8_t)ret; | 130 | *val = (uint8_t)ret; |
132 | return 0; | 131 | return 0; |
133 | } | 132 | } |
134 | 133 | ||
135 | static int adp8860_write(struct i2c_client *client, u8 reg, u8 val) | 134 | static int adp8860_write(struct i2c_client *client, u8 reg, u8 val) |
136 | { | 135 | { |
137 | return i2c_smbus_write_byte_data(client, reg, val); | 136 | return i2c_smbus_write_byte_data(client, reg, val); |
138 | } | 137 | } |
139 | 138 | ||
140 | static int adp8860_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask) | 139 | static int adp8860_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask) |
141 | { | 140 | { |
142 | struct adp8860_bl *data = i2c_get_clientdata(client); | 141 | struct adp8860_bl *data = i2c_get_clientdata(client); |
143 | uint8_t reg_val; | 142 | uint8_t reg_val; |
144 | int ret; | 143 | int ret; |
145 | 144 | ||
146 | mutex_lock(&data->lock); | 145 | mutex_lock(&data->lock); |
147 | 146 | ||
148 | ret = adp8860_read(client, reg, ®_val); | 147 | ret = adp8860_read(client, reg, ®_val); |
149 | 148 | ||
150 | if (!ret && ((reg_val & bit_mask) == 0)) { | 149 | if (!ret && ((reg_val & bit_mask) == 0)) { |
151 | reg_val |= bit_mask; | 150 | reg_val |= bit_mask; |
152 | ret = adp8860_write(client, reg, reg_val); | 151 | ret = adp8860_write(client, reg, reg_val); |
153 | } | 152 | } |
154 | 153 | ||
155 | mutex_unlock(&data->lock); | 154 | mutex_unlock(&data->lock); |
156 | return ret; | 155 | return ret; |
157 | } | 156 | } |
158 | 157 | ||
159 | static int adp8860_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask) | 158 | static int adp8860_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask) |
160 | { | 159 | { |
161 | struct adp8860_bl *data = i2c_get_clientdata(client); | 160 | struct adp8860_bl *data = i2c_get_clientdata(client); |
162 | uint8_t reg_val; | 161 | uint8_t reg_val; |
163 | int ret; | 162 | int ret; |
164 | 163 | ||
165 | mutex_lock(&data->lock); | 164 | mutex_lock(&data->lock); |
166 | 165 | ||
167 | ret = adp8860_read(client, reg, ®_val); | 166 | ret = adp8860_read(client, reg, ®_val); |
168 | 167 | ||
169 | if (!ret && (reg_val & bit_mask)) { | 168 | if (!ret && (reg_val & bit_mask)) { |
170 | reg_val &= ~bit_mask; | 169 | reg_val &= ~bit_mask; |
171 | ret = adp8860_write(client, reg, reg_val); | 170 | ret = adp8860_write(client, reg, reg_val); |
172 | } | 171 | } |
173 | 172 | ||
174 | mutex_unlock(&data->lock); | 173 | mutex_unlock(&data->lock); |
175 | return ret; | 174 | return ret; |
176 | } | 175 | } |
177 | 176 | ||
178 | /* | 177 | /* |
179 | * Independent sink / LED | 178 | * Independent sink / LED |
180 | */ | 179 | */ |
181 | #if defined(ADP8860_USE_LEDS) | 180 | #if defined(ADP8860_USE_LEDS) |
182 | static void adp8860_led_work(struct work_struct *work) | 181 | static void adp8860_led_work(struct work_struct *work) |
183 | { | 182 | { |
184 | struct adp8860_led *led = container_of(work, struct adp8860_led, work); | 183 | struct adp8860_led *led = container_of(work, struct adp8860_led, work); |
185 | adp8860_write(led->client, ADP8860_ISC1 - led->id + 1, | 184 | adp8860_write(led->client, ADP8860_ISC1 - led->id + 1, |
186 | led->new_brightness >> 1); | 185 | led->new_brightness >> 1); |
187 | } | 186 | } |
188 | 187 | ||
189 | static void adp8860_led_set(struct led_classdev *led_cdev, | 188 | static void adp8860_led_set(struct led_classdev *led_cdev, |
190 | enum led_brightness value) | 189 | enum led_brightness value) |
191 | { | 190 | { |
192 | struct adp8860_led *led; | 191 | struct adp8860_led *led; |
193 | 192 | ||
194 | led = container_of(led_cdev, struct adp8860_led, cdev); | 193 | led = container_of(led_cdev, struct adp8860_led, cdev); |
195 | led->new_brightness = value; | 194 | led->new_brightness = value; |
196 | schedule_work(&led->work); | 195 | schedule_work(&led->work); |
197 | } | 196 | } |
198 | 197 | ||
199 | static int adp8860_led_setup(struct adp8860_led *led) | 198 | static int adp8860_led_setup(struct adp8860_led *led) |
200 | { | 199 | { |
201 | struct i2c_client *client = led->client; | 200 | struct i2c_client *client = led->client; |
202 | int ret = 0; | 201 | int ret = 0; |
203 | 202 | ||
204 | ret = adp8860_write(client, ADP8860_ISC1 - led->id + 1, 0); | 203 | ret = adp8860_write(client, ADP8860_ISC1 - led->id + 1, 0); |
205 | ret |= adp8860_set_bits(client, ADP8860_ISCC, 1 << (led->id - 1)); | 204 | ret |= adp8860_set_bits(client, ADP8860_ISCC, 1 << (led->id - 1)); |
206 | 205 | ||
207 | if (led->id > 4) | 206 | if (led->id > 4) |
208 | ret |= adp8860_set_bits(client, ADP8860_ISCT1, | 207 | ret |= adp8860_set_bits(client, ADP8860_ISCT1, |
209 | (led->flags & 0x3) << ((led->id - 5) * 2)); | 208 | (led->flags & 0x3) << ((led->id - 5) * 2)); |
210 | else | 209 | else |
211 | ret |= adp8860_set_bits(client, ADP8860_ISCT2, | 210 | ret |= adp8860_set_bits(client, ADP8860_ISCT2, |
212 | (led->flags & 0x3) << ((led->id - 1) * 2)); | 211 | (led->flags & 0x3) << ((led->id - 1) * 2)); |
213 | 212 | ||
214 | return ret; | 213 | return ret; |
215 | } | 214 | } |
216 | 215 | ||
217 | static int __devinit adp8860_led_probe(struct i2c_client *client) | 216 | static int __devinit adp8860_led_probe(struct i2c_client *client) |
218 | { | 217 | { |
219 | struct adp8860_backlight_platform_data *pdata = | 218 | struct adp8860_backlight_platform_data *pdata = |
220 | client->dev.platform_data; | 219 | client->dev.platform_data; |
221 | struct adp8860_bl *data = i2c_get_clientdata(client); | 220 | struct adp8860_bl *data = i2c_get_clientdata(client); |
222 | struct adp8860_led *led, *led_dat; | 221 | struct adp8860_led *led, *led_dat; |
223 | struct led_info *cur_led; | 222 | struct led_info *cur_led; |
224 | int ret, i; | 223 | int ret, i; |
225 | 224 | ||
226 | led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL); | 225 | led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL); |
227 | if (led == NULL) { | 226 | if (led == NULL) { |
228 | dev_err(&client->dev, "failed to alloc memory\n"); | 227 | dev_err(&client->dev, "failed to alloc memory\n"); |
229 | return -ENOMEM; | 228 | return -ENOMEM; |
230 | } | 229 | } |
231 | 230 | ||
232 | ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law); | 231 | ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law); |
233 | ret = adp8860_write(client, ADP8860_ISCT1, | 232 | ret = adp8860_write(client, ADP8860_ISCT1, |
234 | (pdata->led_on_time & 0x3) << 6); | 233 | (pdata->led_on_time & 0x3) << 6); |
235 | ret |= adp8860_write(client, ADP8860_ISCF, | 234 | ret |= adp8860_write(client, ADP8860_ISCF, |
236 | FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); | 235 | FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); |
237 | 236 | ||
238 | if (ret) { | 237 | if (ret) { |
239 | dev_err(&client->dev, "failed to write\n"); | 238 | dev_err(&client->dev, "failed to write\n"); |
240 | goto err_free; | 239 | goto err_free; |
241 | } | 240 | } |
242 | 241 | ||
243 | for (i = 0; i < pdata->num_leds; ++i) { | 242 | for (i = 0; i < pdata->num_leds; ++i) { |
244 | cur_led = &pdata->leds[i]; | 243 | cur_led = &pdata->leds[i]; |
245 | led_dat = &led[i]; | 244 | led_dat = &led[i]; |
246 | 245 | ||
247 | led_dat->id = cur_led->flags & ADP8860_FLAG_LED_MASK; | 246 | led_dat->id = cur_led->flags & ADP8860_FLAG_LED_MASK; |
248 | 247 | ||
249 | if (led_dat->id > 7 || led_dat->id < 1) { | 248 | if (led_dat->id > 7 || led_dat->id < 1) { |
250 | dev_err(&client->dev, "Invalid LED ID %d\n", | 249 | dev_err(&client->dev, "Invalid LED ID %d\n", |
251 | led_dat->id); | 250 | led_dat->id); |
252 | goto err; | 251 | goto err; |
253 | } | 252 | } |
254 | 253 | ||
255 | if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) { | 254 | if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) { |
256 | dev_err(&client->dev, "LED %d used by Backlight\n", | 255 | dev_err(&client->dev, "LED %d used by Backlight\n", |
257 | led_dat->id); | 256 | led_dat->id); |
258 | goto err; | 257 | goto err; |
259 | } | 258 | } |
260 | 259 | ||
261 | led_dat->cdev.name = cur_led->name; | 260 | led_dat->cdev.name = cur_led->name; |
262 | led_dat->cdev.default_trigger = cur_led->default_trigger; | 261 | led_dat->cdev.default_trigger = cur_led->default_trigger; |
263 | led_dat->cdev.brightness_set = adp8860_led_set; | 262 | led_dat->cdev.brightness_set = adp8860_led_set; |
264 | led_dat->cdev.brightness = LED_OFF; | 263 | led_dat->cdev.brightness = LED_OFF; |
265 | led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; | 264 | led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; |
266 | led_dat->client = client; | 265 | led_dat->client = client; |
267 | led_dat->new_brightness = LED_OFF; | 266 | led_dat->new_brightness = LED_OFF; |
268 | INIT_WORK(&led_dat->work, adp8860_led_work); | 267 | INIT_WORK(&led_dat->work, adp8860_led_work); |
269 | 268 | ||
270 | ret = led_classdev_register(&client->dev, &led_dat->cdev); | 269 | ret = led_classdev_register(&client->dev, &led_dat->cdev); |
271 | if (ret) { | 270 | if (ret) { |
272 | dev_err(&client->dev, "failed to register LED %d\n", | 271 | dev_err(&client->dev, "failed to register LED %d\n", |
273 | led_dat->id); | 272 | led_dat->id); |
274 | goto err; | 273 | goto err; |
275 | } | 274 | } |
276 | 275 | ||
277 | ret = adp8860_led_setup(led_dat); | 276 | ret = adp8860_led_setup(led_dat); |
278 | if (ret) { | 277 | if (ret) { |
279 | dev_err(&client->dev, "failed to write\n"); | 278 | dev_err(&client->dev, "failed to write\n"); |
280 | i++; | 279 | i++; |
281 | goto err; | 280 | goto err; |
282 | } | 281 | } |
283 | } | 282 | } |
284 | 283 | ||
285 | data->led = led; | 284 | data->led = led; |
286 | 285 | ||
287 | return 0; | 286 | return 0; |
288 | 287 | ||
289 | err: | 288 | err: |
290 | for (i = i - 1; i >= 0; --i) { | 289 | for (i = i - 1; i >= 0; --i) { |
291 | led_classdev_unregister(&led[i].cdev); | 290 | led_classdev_unregister(&led[i].cdev); |
292 | cancel_work_sync(&led[i].work); | 291 | cancel_work_sync(&led[i].work); |
293 | } | 292 | } |
294 | 293 | ||
295 | err_free: | 294 | err_free: |
296 | kfree(led); | 295 | kfree(led); |
297 | 296 | ||
298 | return ret; | 297 | return ret; |
299 | } | 298 | } |
300 | 299 | ||
301 | static int __devexit adp8860_led_remove(struct i2c_client *client) | 300 | static int __devexit adp8860_led_remove(struct i2c_client *client) |
302 | { | 301 | { |
303 | struct adp8860_backlight_platform_data *pdata = | 302 | struct adp8860_backlight_platform_data *pdata = |
304 | client->dev.platform_data; | 303 | client->dev.platform_data; |
305 | struct adp8860_bl *data = i2c_get_clientdata(client); | 304 | struct adp8860_bl *data = i2c_get_clientdata(client); |
306 | int i; | 305 | int i; |
307 | 306 | ||
308 | for (i = 0; i < pdata->num_leds; i++) { | 307 | for (i = 0; i < pdata->num_leds; i++) { |
309 | led_classdev_unregister(&data->led[i].cdev); | 308 | led_classdev_unregister(&data->led[i].cdev); |
310 | cancel_work_sync(&data->led[i].work); | 309 | cancel_work_sync(&data->led[i].work); |
311 | } | 310 | } |
312 | 311 | ||
313 | kfree(data->led); | 312 | kfree(data->led); |
314 | return 0; | 313 | return 0; |
315 | } | 314 | } |
316 | #else | 315 | #else |
317 | static int __devinit adp8860_led_probe(struct i2c_client *client) | 316 | static int __devinit adp8860_led_probe(struct i2c_client *client) |
318 | { | 317 | { |
319 | return 0; | 318 | return 0; |
320 | } | 319 | } |
321 | 320 | ||
322 | static int __devexit adp8860_led_remove(struct i2c_client *client) | 321 | static int __devexit adp8860_led_remove(struct i2c_client *client) |
323 | { | 322 | { |
324 | return 0; | 323 | return 0; |
325 | } | 324 | } |
326 | #endif | 325 | #endif |
327 | 326 | ||
328 | static int adp8860_bl_set(struct backlight_device *bl, int brightness) | 327 | static int adp8860_bl_set(struct backlight_device *bl, int brightness) |
329 | { | 328 | { |
330 | struct adp8860_bl *data = bl_get_data(bl); | 329 | struct adp8860_bl *data = bl_get_data(bl); |
331 | struct i2c_client *client = data->client; | 330 | struct i2c_client *client = data->client; |
332 | int ret = 0; | 331 | int ret = 0; |
333 | 332 | ||
334 | if (data->en_ambl_sens) { | 333 | if (data->en_ambl_sens) { |
335 | if ((brightness > 0) && (brightness < ADP8860_MAX_BRIGHTNESS)) { | 334 | if ((brightness > 0) && (brightness < ADP8860_MAX_BRIGHTNESS)) { |
336 | /* Disable Ambient Light auto adjust */ | 335 | /* Disable Ambient Light auto adjust */ |
337 | ret |= adp8860_clr_bits(client, ADP8860_MDCR, | 336 | ret |= adp8860_clr_bits(client, ADP8860_MDCR, |
338 | CMP_AUTOEN); | 337 | CMP_AUTOEN); |
339 | ret |= adp8860_write(client, ADP8860_BLMX1, brightness); | 338 | ret |= adp8860_write(client, ADP8860_BLMX1, brightness); |
340 | } else { | 339 | } else { |
341 | /* | 340 | /* |
342 | * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust | 341 | * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust |
343 | * restore daylight l1 sysfs brightness | 342 | * restore daylight l1 sysfs brightness |
344 | */ | 343 | */ |
345 | ret |= adp8860_write(client, ADP8860_BLMX1, | 344 | ret |= adp8860_write(client, ADP8860_BLMX1, |
346 | data->cached_daylight_max); | 345 | data->cached_daylight_max); |
347 | ret |= adp8860_set_bits(client, ADP8860_MDCR, | 346 | ret |= adp8860_set_bits(client, ADP8860_MDCR, |
348 | CMP_AUTOEN); | 347 | CMP_AUTOEN); |
349 | } | 348 | } |
350 | } else | 349 | } else |
351 | ret |= adp8860_write(client, ADP8860_BLMX1, brightness); | 350 | ret |= adp8860_write(client, ADP8860_BLMX1, brightness); |
352 | 351 | ||
353 | if (data->current_brightness && brightness == 0) | 352 | if (data->current_brightness && brightness == 0) |
354 | ret |= adp8860_set_bits(client, | 353 | ret |= adp8860_set_bits(client, |
355 | ADP8860_MDCR, DIM_EN); | 354 | ADP8860_MDCR, DIM_EN); |
356 | else if (data->current_brightness == 0 && brightness) | 355 | else if (data->current_brightness == 0 && brightness) |
357 | ret |= adp8860_clr_bits(client, | 356 | ret |= adp8860_clr_bits(client, |
358 | ADP8860_MDCR, DIM_EN); | 357 | ADP8860_MDCR, DIM_EN); |
359 | 358 | ||
360 | if (!ret) | 359 | if (!ret) |
361 | data->current_brightness = brightness; | 360 | data->current_brightness = brightness; |
362 | 361 | ||
363 | return ret; | 362 | return ret; |
364 | } | 363 | } |
365 | 364 | ||
366 | static int adp8860_bl_update_status(struct backlight_device *bl) | 365 | static int adp8860_bl_update_status(struct backlight_device *bl) |
367 | { | 366 | { |
368 | int brightness = bl->props.brightness; | 367 | int brightness = bl->props.brightness; |
369 | if (bl->props.power != FB_BLANK_UNBLANK) | 368 | if (bl->props.power != FB_BLANK_UNBLANK) |
370 | brightness = 0; | 369 | brightness = 0; |
371 | 370 | ||
372 | if (bl->props.fb_blank != FB_BLANK_UNBLANK) | 371 | if (bl->props.fb_blank != FB_BLANK_UNBLANK) |
373 | brightness = 0; | 372 | brightness = 0; |
374 | 373 | ||
375 | return adp8860_bl_set(bl, brightness); | 374 | return adp8860_bl_set(bl, brightness); |
376 | } | 375 | } |
377 | 376 | ||
378 | static int adp8860_bl_get_brightness(struct backlight_device *bl) | 377 | static int adp8860_bl_get_brightness(struct backlight_device *bl) |
379 | { | 378 | { |
380 | struct adp8860_bl *data = bl_get_data(bl); | 379 | struct adp8860_bl *data = bl_get_data(bl); |
381 | 380 | ||
382 | return data->current_brightness; | 381 | return data->current_brightness; |
383 | } | 382 | } |
384 | 383 | ||
385 | static const struct backlight_ops adp8860_bl_ops = { | 384 | static const struct backlight_ops adp8860_bl_ops = { |
386 | .update_status = adp8860_bl_update_status, | 385 | .update_status = adp8860_bl_update_status, |
387 | .get_brightness = adp8860_bl_get_brightness, | 386 | .get_brightness = adp8860_bl_get_brightness, |
388 | }; | 387 | }; |
389 | 388 | ||
390 | static int adp8860_bl_setup(struct backlight_device *bl) | 389 | static int adp8860_bl_setup(struct backlight_device *bl) |
391 | { | 390 | { |
392 | struct adp8860_bl *data = bl_get_data(bl); | 391 | struct adp8860_bl *data = bl_get_data(bl); |
393 | struct i2c_client *client = data->client; | 392 | struct i2c_client *client = data->client; |
394 | struct adp8860_backlight_platform_data *pdata = data->pdata; | 393 | struct adp8860_backlight_platform_data *pdata = data->pdata; |
395 | int ret = 0; | 394 | int ret = 0; |
396 | 395 | ||
397 | ret |= adp8860_write(client, ADP8860_BLSEN, ~pdata->bl_led_assign); | 396 | ret |= adp8860_write(client, ADP8860_BLSEN, ~pdata->bl_led_assign); |
398 | ret |= adp8860_write(client, ADP8860_BLMX1, pdata->l1_daylight_max); | 397 | ret |= adp8860_write(client, ADP8860_BLMX1, pdata->l1_daylight_max); |
399 | ret |= adp8860_write(client, ADP8860_BLDM1, pdata->l1_daylight_dim); | 398 | ret |= adp8860_write(client, ADP8860_BLDM1, pdata->l1_daylight_dim); |
400 | 399 | ||
401 | if (data->en_ambl_sens) { | 400 | if (data->en_ambl_sens) { |
402 | data->cached_daylight_max = pdata->l1_daylight_max; | 401 | data->cached_daylight_max = pdata->l1_daylight_max; |
403 | ret |= adp8860_write(client, ADP8860_BLMX2, | 402 | ret |= adp8860_write(client, ADP8860_BLMX2, |
404 | pdata->l2_office_max); | 403 | pdata->l2_office_max); |
405 | ret |= adp8860_write(client, ADP8860_BLDM2, | 404 | ret |= adp8860_write(client, ADP8860_BLDM2, |
406 | pdata->l2_office_dim); | 405 | pdata->l2_office_dim); |
407 | ret |= adp8860_write(client, ADP8860_BLMX3, | 406 | ret |= adp8860_write(client, ADP8860_BLMX3, |
408 | pdata->l3_dark_max); | 407 | pdata->l3_dark_max); |
409 | ret |= adp8860_write(client, ADP8860_BLDM3, | 408 | ret |= adp8860_write(client, ADP8860_BLDM3, |
410 | pdata->l3_dark_dim); | 409 | pdata->l3_dark_dim); |
411 | 410 | ||
412 | ret |= adp8860_write(client, ADP8860_L2_TRP, pdata->l2_trip); | 411 | ret |= adp8860_write(client, ADP8860_L2_TRP, pdata->l2_trip); |
413 | ret |= adp8860_write(client, ADP8860_L2_HYS, pdata->l2_hyst); | 412 | ret |= adp8860_write(client, ADP8860_L2_HYS, pdata->l2_hyst); |
414 | ret |= adp8860_write(client, ADP8860_L3_TRP, pdata->l3_trip); | 413 | ret |= adp8860_write(client, ADP8860_L3_TRP, pdata->l3_trip); |
415 | ret |= adp8860_write(client, ADP8860_L3_HYS, pdata->l3_hyst); | 414 | ret |= adp8860_write(client, ADP8860_L3_HYS, pdata->l3_hyst); |
416 | ret |= adp8860_write(client, ADP8860_CCFG, L2_EN | L3_EN | | 415 | ret |= adp8860_write(client, ADP8860_CCFG, L2_EN | L3_EN | |
417 | ALS_CCFG_VAL(pdata->abml_filt)); | 416 | ALS_CCFG_VAL(pdata->abml_filt)); |
418 | } | 417 | } |
419 | 418 | ||
420 | ret |= adp8860_write(client, ADP8860_CFGR, | 419 | ret |= adp8860_write(client, ADP8860_CFGR, |
421 | BL_CFGR_VAL(pdata->bl_fade_law, 0)); | 420 | BL_CFGR_VAL(pdata->bl_fade_law, 0)); |
422 | 421 | ||
423 | ret |= adp8860_write(client, ADP8860_BLFR, FADE_VAL(pdata->bl_fade_in, | 422 | ret |= adp8860_write(client, ADP8860_BLFR, FADE_VAL(pdata->bl_fade_in, |
424 | pdata->bl_fade_out)); | 423 | pdata->bl_fade_out)); |
425 | 424 | ||
426 | ret |= adp8860_set_bits(client, ADP8860_MDCR, BLEN | DIM_EN | NSTBY | | 425 | ret |= adp8860_set_bits(client, ADP8860_MDCR, BLEN | DIM_EN | NSTBY | |
427 | (data->gdwn_dis ? GDWN_DIS : 0)); | 426 | (data->gdwn_dis ? GDWN_DIS : 0)); |
428 | 427 | ||
429 | return ret; | 428 | return ret; |
430 | } | 429 | } |
431 | 430 | ||
432 | static ssize_t adp8860_show(struct device *dev, char *buf, int reg) | 431 | static ssize_t adp8860_show(struct device *dev, char *buf, int reg) |
433 | { | 432 | { |
434 | struct adp8860_bl *data = dev_get_drvdata(dev); | 433 | struct adp8860_bl *data = dev_get_drvdata(dev); |
435 | int error; | 434 | int error; |
436 | uint8_t reg_val; | 435 | uint8_t reg_val; |
437 | 436 | ||
438 | mutex_lock(&data->lock); | 437 | mutex_lock(&data->lock); |
439 | error = adp8860_read(data->client, reg, ®_val); | 438 | error = adp8860_read(data->client, reg, ®_val); |
440 | mutex_unlock(&data->lock); | 439 | mutex_unlock(&data->lock); |
441 | 440 | ||
442 | if (error < 0) | 441 | if (error < 0) |
443 | return error; | 442 | return error; |
444 | 443 | ||
445 | return sprintf(buf, "%u\n", reg_val); | 444 | return sprintf(buf, "%u\n", reg_val); |
446 | } | 445 | } |
447 | 446 | ||
448 | static ssize_t adp8860_store(struct device *dev, const char *buf, | 447 | static ssize_t adp8860_store(struct device *dev, const char *buf, |
449 | size_t count, int reg) | 448 | size_t count, int reg) |
450 | { | 449 | { |
451 | struct adp8860_bl *data = dev_get_drvdata(dev); | 450 | struct adp8860_bl *data = dev_get_drvdata(dev); |
452 | unsigned long val; | 451 | unsigned long val; |
453 | int ret; | 452 | int ret; |
454 | 453 | ||
455 | ret = strict_strtoul(buf, 10, &val); | 454 | ret = strict_strtoul(buf, 10, &val); |
456 | if (ret) | 455 | if (ret) |
457 | return ret; | 456 | return ret; |
458 | 457 | ||
459 | mutex_lock(&data->lock); | 458 | mutex_lock(&data->lock); |
460 | adp8860_write(data->client, reg, val); | 459 | adp8860_write(data->client, reg, val); |
461 | mutex_unlock(&data->lock); | 460 | mutex_unlock(&data->lock); |
462 | 461 | ||
463 | return count; | 462 | return count; |
464 | } | 463 | } |
465 | 464 | ||
466 | static ssize_t adp8860_bl_l3_dark_max_show(struct device *dev, | 465 | static ssize_t adp8860_bl_l3_dark_max_show(struct device *dev, |
467 | struct device_attribute *attr, char *buf) | 466 | struct device_attribute *attr, char *buf) |
468 | { | 467 | { |
469 | return adp8860_show(dev, buf, ADP8860_BLMX3); | 468 | return adp8860_show(dev, buf, ADP8860_BLMX3); |
470 | } | 469 | } |
471 | 470 | ||
472 | static ssize_t adp8860_bl_l3_dark_max_store(struct device *dev, | 471 | static ssize_t adp8860_bl_l3_dark_max_store(struct device *dev, |
473 | struct device_attribute *attr, const char *buf, size_t count) | 472 | struct device_attribute *attr, const char *buf, size_t count) |
474 | { | 473 | { |
475 | return adp8860_store(dev, buf, count, ADP8860_BLMX3); | 474 | return adp8860_store(dev, buf, count, ADP8860_BLMX3); |
476 | } | 475 | } |
477 | 476 | ||
478 | static DEVICE_ATTR(l3_dark_max, 0664, adp8860_bl_l3_dark_max_show, | 477 | static DEVICE_ATTR(l3_dark_max, 0664, adp8860_bl_l3_dark_max_show, |
479 | adp8860_bl_l3_dark_max_store); | 478 | adp8860_bl_l3_dark_max_store); |
480 | 479 | ||
481 | static ssize_t adp8860_bl_l2_office_max_show(struct device *dev, | 480 | static ssize_t adp8860_bl_l2_office_max_show(struct device *dev, |
482 | struct device_attribute *attr, char *buf) | 481 | struct device_attribute *attr, char *buf) |
483 | { | 482 | { |
484 | return adp8860_show(dev, buf, ADP8860_BLMX2); | 483 | return adp8860_show(dev, buf, ADP8860_BLMX2); |
485 | } | 484 | } |
486 | 485 | ||
487 | static ssize_t adp8860_bl_l2_office_max_store(struct device *dev, | 486 | static ssize_t adp8860_bl_l2_office_max_store(struct device *dev, |
488 | struct device_attribute *attr, const char *buf, size_t count) | 487 | struct device_attribute *attr, const char *buf, size_t count) |
489 | { | 488 | { |
490 | return adp8860_store(dev, buf, count, ADP8860_BLMX2); | 489 | return adp8860_store(dev, buf, count, ADP8860_BLMX2); |
491 | } | 490 | } |
492 | static DEVICE_ATTR(l2_office_max, 0664, adp8860_bl_l2_office_max_show, | 491 | static DEVICE_ATTR(l2_office_max, 0664, adp8860_bl_l2_office_max_show, |
493 | adp8860_bl_l2_office_max_store); | 492 | adp8860_bl_l2_office_max_store); |
494 | 493 | ||
495 | static ssize_t adp8860_bl_l1_daylight_max_show(struct device *dev, | 494 | static ssize_t adp8860_bl_l1_daylight_max_show(struct device *dev, |
496 | struct device_attribute *attr, char *buf) | 495 | struct device_attribute *attr, char *buf) |
497 | { | 496 | { |
498 | return adp8860_show(dev, buf, ADP8860_BLMX1); | 497 | return adp8860_show(dev, buf, ADP8860_BLMX1); |
499 | } | 498 | } |
500 | 499 | ||
501 | static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev, | 500 | static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev, |
502 | struct device_attribute *attr, const char *buf, size_t count) | 501 | struct device_attribute *attr, const char *buf, size_t count) |
503 | { | 502 | { |
504 | struct adp8860_bl *data = dev_get_drvdata(dev); | 503 | struct adp8860_bl *data = dev_get_drvdata(dev); |
505 | int ret = strict_strtoul(buf, 10, &data->cached_daylight_max); | 504 | int ret = strict_strtoul(buf, 10, &data->cached_daylight_max); |
506 | if (ret) | 505 | if (ret) |
507 | return ret; | 506 | return ret; |
508 | 507 | ||
509 | return adp8860_store(dev, buf, count, ADP8860_BLMX1); | 508 | return adp8860_store(dev, buf, count, ADP8860_BLMX1); |
510 | } | 509 | } |
511 | static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show, | 510 | static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show, |
512 | adp8860_bl_l1_daylight_max_store); | 511 | adp8860_bl_l1_daylight_max_store); |
513 | 512 | ||
514 | static ssize_t adp8860_bl_l3_dark_dim_show(struct device *dev, | 513 | static ssize_t adp8860_bl_l3_dark_dim_show(struct device *dev, |
515 | struct device_attribute *attr, char *buf) | 514 | struct device_attribute *attr, char *buf) |
516 | { | 515 | { |
517 | return adp8860_show(dev, buf, ADP8860_BLDM3); | 516 | return adp8860_show(dev, buf, ADP8860_BLDM3); |
518 | } | 517 | } |
519 | 518 | ||
520 | static ssize_t adp8860_bl_l3_dark_dim_store(struct device *dev, | 519 | static ssize_t adp8860_bl_l3_dark_dim_store(struct device *dev, |
521 | struct device_attribute *attr, | 520 | struct device_attribute *attr, |
522 | const char *buf, size_t count) | 521 | const char *buf, size_t count) |
523 | { | 522 | { |
524 | return adp8860_store(dev, buf, count, ADP8860_BLDM3); | 523 | return adp8860_store(dev, buf, count, ADP8860_BLDM3); |
525 | } | 524 | } |
526 | static DEVICE_ATTR(l3_dark_dim, 0664, adp8860_bl_l3_dark_dim_show, | 525 | static DEVICE_ATTR(l3_dark_dim, 0664, adp8860_bl_l3_dark_dim_show, |
527 | adp8860_bl_l3_dark_dim_store); | 526 | adp8860_bl_l3_dark_dim_store); |
528 | 527 | ||
529 | static ssize_t adp8860_bl_l2_office_dim_show(struct device *dev, | 528 | static ssize_t adp8860_bl_l2_office_dim_show(struct device *dev, |
530 | struct device_attribute *attr, char *buf) | 529 | struct device_attribute *attr, char *buf) |
531 | { | 530 | { |
532 | return adp8860_show(dev, buf, ADP8860_BLDM2); | 531 | return adp8860_show(dev, buf, ADP8860_BLDM2); |
533 | } | 532 | } |
534 | 533 | ||
535 | static ssize_t adp8860_bl_l2_office_dim_store(struct device *dev, | 534 | static ssize_t adp8860_bl_l2_office_dim_store(struct device *dev, |
536 | struct device_attribute *attr, | 535 | struct device_attribute *attr, |
537 | const char *buf, size_t count) | 536 | const char *buf, size_t count) |
538 | { | 537 | { |
539 | return adp8860_store(dev, buf, count, ADP8860_BLDM2); | 538 | return adp8860_store(dev, buf, count, ADP8860_BLDM2); |
540 | } | 539 | } |
541 | static DEVICE_ATTR(l2_office_dim, 0664, adp8860_bl_l2_office_dim_show, | 540 | static DEVICE_ATTR(l2_office_dim, 0664, adp8860_bl_l2_office_dim_show, |
542 | adp8860_bl_l2_office_dim_store); | 541 | adp8860_bl_l2_office_dim_store); |
543 | 542 | ||
544 | static ssize_t adp8860_bl_l1_daylight_dim_show(struct device *dev, | 543 | static ssize_t adp8860_bl_l1_daylight_dim_show(struct device *dev, |
545 | struct device_attribute *attr, char *buf) | 544 | struct device_attribute *attr, char *buf) |
546 | { | 545 | { |
547 | return adp8860_show(dev, buf, ADP8860_BLDM1); | 546 | return adp8860_show(dev, buf, ADP8860_BLDM1); |
548 | } | 547 | } |
549 | 548 | ||
550 | static ssize_t adp8860_bl_l1_daylight_dim_store(struct device *dev, | 549 | static ssize_t adp8860_bl_l1_daylight_dim_store(struct device *dev, |
551 | struct device_attribute *attr, | 550 | struct device_attribute *attr, |
552 | const char *buf, size_t count) | 551 | const char *buf, size_t count) |
553 | { | 552 | { |
554 | return adp8860_store(dev, buf, count, ADP8860_BLDM1); | 553 | return adp8860_store(dev, buf, count, ADP8860_BLDM1); |
555 | } | 554 | } |
556 | static DEVICE_ATTR(l1_daylight_dim, 0664, adp8860_bl_l1_daylight_dim_show, | 555 | static DEVICE_ATTR(l1_daylight_dim, 0664, adp8860_bl_l1_daylight_dim_show, |
557 | adp8860_bl_l1_daylight_dim_store); | 556 | adp8860_bl_l1_daylight_dim_store); |
558 | 557 | ||
559 | #ifdef ADP8860_EXT_FEATURES | 558 | #ifdef ADP8860_EXT_FEATURES |
560 | static ssize_t adp8860_bl_ambient_light_level_show(struct device *dev, | 559 | static ssize_t adp8860_bl_ambient_light_level_show(struct device *dev, |
561 | struct device_attribute *attr, char *buf) | 560 | struct device_attribute *attr, char *buf) |
562 | { | 561 | { |
563 | struct adp8860_bl *data = dev_get_drvdata(dev); | 562 | struct adp8860_bl *data = dev_get_drvdata(dev); |
564 | int error; | 563 | int error; |
565 | uint8_t reg_val; | 564 | uint8_t reg_val; |
566 | uint16_t ret_val; | 565 | uint16_t ret_val; |
567 | 566 | ||
568 | mutex_lock(&data->lock); | 567 | mutex_lock(&data->lock); |
569 | error = adp8860_read(data->client, ADP8860_PH1LEVL, ®_val); | 568 | error = adp8860_read(data->client, ADP8860_PH1LEVL, ®_val); |
570 | ret_val = reg_val; | 569 | ret_val = reg_val; |
571 | error |= adp8860_read(data->client, ADP8860_PH1LEVH, ®_val); | 570 | error |= adp8860_read(data->client, ADP8860_PH1LEVH, ®_val); |
572 | mutex_unlock(&data->lock); | 571 | mutex_unlock(&data->lock); |
573 | 572 | ||
574 | if (error < 0) | 573 | if (error < 0) |
575 | return error; | 574 | return error; |
576 | 575 | ||
577 | /* Return 13-bit conversion value for the first light sensor */ | 576 | /* Return 13-bit conversion value for the first light sensor */ |
578 | ret_val += (reg_val & 0x1F) << 8; | 577 | ret_val += (reg_val & 0x1F) << 8; |
579 | 578 | ||
580 | return sprintf(buf, "%u\n", ret_val); | 579 | return sprintf(buf, "%u\n", ret_val); |
581 | } | 580 | } |
582 | static DEVICE_ATTR(ambient_light_level, 0444, | 581 | static DEVICE_ATTR(ambient_light_level, 0444, |
583 | adp8860_bl_ambient_light_level_show, NULL); | 582 | adp8860_bl_ambient_light_level_show, NULL); |
584 | 583 | ||
585 | static ssize_t adp8860_bl_ambient_light_zone_show(struct device *dev, | 584 | static ssize_t adp8860_bl_ambient_light_zone_show(struct device *dev, |
586 | struct device_attribute *attr, char *buf) | 585 | struct device_attribute *attr, char *buf) |
587 | { | 586 | { |
588 | struct adp8860_bl *data = dev_get_drvdata(dev); | 587 | struct adp8860_bl *data = dev_get_drvdata(dev); |
589 | int error; | 588 | int error; |
590 | uint8_t reg_val; | 589 | uint8_t reg_val; |
591 | 590 | ||
592 | mutex_lock(&data->lock); | 591 | mutex_lock(&data->lock); |
593 | error = adp8860_read(data->client, ADP8860_CFGR, ®_val); | 592 | error = adp8860_read(data->client, ADP8860_CFGR, ®_val); |
594 | mutex_unlock(&data->lock); | 593 | mutex_unlock(&data->lock); |
595 | 594 | ||
596 | if (error < 0) | 595 | if (error < 0) |
597 | return error; | 596 | return error; |
598 | 597 | ||
599 | return sprintf(buf, "%u\n", | 598 | return sprintf(buf, "%u\n", |
600 | ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); | 599 | ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); |
601 | } | 600 | } |
602 | 601 | ||
603 | static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev, | 602 | static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev, |
604 | struct device_attribute *attr, | 603 | struct device_attribute *attr, |
605 | const char *buf, size_t count) | 604 | const char *buf, size_t count) |
606 | { | 605 | { |
607 | struct adp8860_bl *data = dev_get_drvdata(dev); | 606 | struct adp8860_bl *data = dev_get_drvdata(dev); |
608 | unsigned long val; | 607 | unsigned long val; |
609 | uint8_t reg_val; | 608 | uint8_t reg_val; |
610 | int ret; | 609 | int ret; |
611 | 610 | ||
612 | ret = strict_strtoul(buf, 10, &val); | 611 | ret = strict_strtoul(buf, 10, &val); |
613 | if (ret) | 612 | if (ret) |
614 | return ret; | 613 | return ret; |
615 | 614 | ||
616 | if (val == 0) { | 615 | if (val == 0) { |
617 | /* Enable automatic ambient light sensing */ | 616 | /* Enable automatic ambient light sensing */ |
618 | adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); | 617 | adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); |
619 | } else if ((val > 0) && (val <= 3)) { | 618 | } else if ((val > 0) && (val <= 3)) { |
620 | /* Disable automatic ambient light sensing */ | 619 | /* Disable automatic ambient light sensing */ |
621 | adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); | 620 | adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); |
622 | 621 | ||
623 | /* Set user supplied ambient light zone */ | 622 | /* Set user supplied ambient light zone */ |
624 | mutex_lock(&data->lock); | 623 | mutex_lock(&data->lock); |
625 | adp8860_read(data->client, ADP8860_CFGR, ®_val); | 624 | adp8860_read(data->client, ADP8860_CFGR, ®_val); |
626 | reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); | 625 | reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); |
627 | reg_val |= (val - 1) << CFGR_BLV_SHIFT; | 626 | reg_val |= (val - 1) << CFGR_BLV_SHIFT; |
628 | adp8860_write(data->client, ADP8860_CFGR, reg_val); | 627 | adp8860_write(data->client, ADP8860_CFGR, reg_val); |
629 | mutex_unlock(&data->lock); | 628 | mutex_unlock(&data->lock); |
630 | } | 629 | } |
631 | 630 | ||
632 | return count; | 631 | return count; |
633 | } | 632 | } |
634 | static DEVICE_ATTR(ambient_light_zone, 0664, | 633 | static DEVICE_ATTR(ambient_light_zone, 0664, |
635 | adp8860_bl_ambient_light_zone_show, | 634 | adp8860_bl_ambient_light_zone_show, |
636 | adp8860_bl_ambient_light_zone_store); | 635 | adp8860_bl_ambient_light_zone_store); |
637 | #endif | 636 | #endif |
638 | 637 | ||
639 | static struct attribute *adp8860_bl_attributes[] = { | 638 | static struct attribute *adp8860_bl_attributes[] = { |
640 | &dev_attr_l3_dark_max.attr, | 639 | &dev_attr_l3_dark_max.attr, |
641 | &dev_attr_l3_dark_dim.attr, | 640 | &dev_attr_l3_dark_dim.attr, |
642 | &dev_attr_l2_office_max.attr, | 641 | &dev_attr_l2_office_max.attr, |
643 | &dev_attr_l2_office_dim.attr, | 642 | &dev_attr_l2_office_dim.attr, |
644 | &dev_attr_l1_daylight_max.attr, | 643 | &dev_attr_l1_daylight_max.attr, |
645 | &dev_attr_l1_daylight_dim.attr, | 644 | &dev_attr_l1_daylight_dim.attr, |
646 | #ifdef ADP8860_EXT_FEATURES | 645 | #ifdef ADP8860_EXT_FEATURES |
647 | &dev_attr_ambient_light_level.attr, | 646 | &dev_attr_ambient_light_level.attr, |
648 | &dev_attr_ambient_light_zone.attr, | 647 | &dev_attr_ambient_light_zone.attr, |
649 | #endif | 648 | #endif |
650 | NULL | 649 | NULL |
651 | }; | 650 | }; |
652 | 651 | ||
653 | static const struct attribute_group adp8860_bl_attr_group = { | 652 | static const struct attribute_group adp8860_bl_attr_group = { |
654 | .attrs = adp8860_bl_attributes, | 653 | .attrs = adp8860_bl_attributes, |
655 | }; | 654 | }; |
656 | 655 | ||
657 | static int __devinit adp8860_probe(struct i2c_client *client, | 656 | static int __devinit adp8860_probe(struct i2c_client *client, |
658 | const struct i2c_device_id *id) | 657 | const struct i2c_device_id *id) |
659 | { | 658 | { |
660 | struct backlight_device *bl; | 659 | struct backlight_device *bl; |
661 | struct adp8860_bl *data; | 660 | struct adp8860_bl *data; |
662 | struct adp8860_backlight_platform_data *pdata = | 661 | struct adp8860_backlight_platform_data *pdata = |
663 | client->dev.platform_data; | 662 | client->dev.platform_data; |
664 | struct backlight_properties props; | 663 | struct backlight_properties props; |
665 | uint8_t reg_val; | 664 | uint8_t reg_val; |
666 | int ret; | 665 | int ret; |
667 | 666 | ||
668 | if (!i2c_check_functionality(client->adapter, | 667 | if (!i2c_check_functionality(client->adapter, |
669 | I2C_FUNC_SMBUS_BYTE_DATA)) { | 668 | I2C_FUNC_SMBUS_BYTE_DATA)) { |
670 | dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); | 669 | dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); |
671 | return -EIO; | 670 | return -EIO; |
672 | } | 671 | } |
673 | 672 | ||
674 | if (!pdata) { | 673 | if (!pdata) { |
675 | dev_err(&client->dev, "no platform data?\n"); | 674 | dev_err(&client->dev, "no platform data?\n"); |
676 | return -EINVAL; | 675 | return -EINVAL; |
677 | } | 676 | } |
678 | 677 | ||
679 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 678 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
680 | if (data == NULL) | 679 | if (data == NULL) |
681 | return -ENOMEM; | 680 | return -ENOMEM; |
682 | 681 | ||
683 | ret = adp8860_read(client, ADP8860_MFDVID, ®_val); | 682 | ret = adp8860_read(client, ADP8860_MFDVID, ®_val); |
684 | if (ret < 0) | 683 | if (ret < 0) |
685 | goto out2; | 684 | goto out2; |
686 | 685 | ||
687 | switch (ADP8860_MANID(reg_val)) { | 686 | switch (ADP8860_MANID(reg_val)) { |
688 | case ADP8863_MANUFID: | 687 | case ADP8863_MANUFID: |
689 | data->gdwn_dis = !!pdata->gdwn_dis; | 688 | data->gdwn_dis = !!pdata->gdwn_dis; |
690 | case ADP8860_MANUFID: | 689 | case ADP8860_MANUFID: |
691 | data->en_ambl_sens = !!pdata->en_ambl_sens; | 690 | data->en_ambl_sens = !!pdata->en_ambl_sens; |
692 | break; | 691 | break; |
693 | case ADP8861_MANUFID: | 692 | case ADP8861_MANUFID: |
694 | data->gdwn_dis = !!pdata->gdwn_dis; | 693 | data->gdwn_dis = !!pdata->gdwn_dis; |
695 | break; | 694 | break; |
696 | default: | 695 | default: |
697 | dev_err(&client->dev, "failed to probe\n"); | 696 | dev_err(&client->dev, "failed to probe\n"); |
698 | ret = -ENODEV; | 697 | ret = -ENODEV; |
699 | goto out2; | 698 | goto out2; |
700 | } | 699 | } |
701 | 700 | ||
702 | /* It's confirmed that the DEVID field is actually a REVID */ | 701 | /* It's confirmed that the DEVID field is actually a REVID */ |
703 | 702 | ||
704 | data->revid = ADP8860_DEVID(reg_val); | 703 | data->revid = ADP8860_DEVID(reg_val); |
705 | data->client = client; | 704 | data->client = client; |
706 | data->pdata = pdata; | 705 | data->pdata = pdata; |
707 | data->id = id->driver_data; | 706 | data->id = id->driver_data; |
708 | data->current_brightness = 0; | 707 | data->current_brightness = 0; |
709 | i2c_set_clientdata(client, data); | 708 | i2c_set_clientdata(client, data); |
710 | 709 | ||
711 | memset(&props, 0, sizeof(props)); | 710 | memset(&props, 0, sizeof(props)); |
712 | props.type = BACKLIGHT_RAW; | 711 | props.type = BACKLIGHT_RAW; |
713 | props.max_brightness = ADP8860_MAX_BRIGHTNESS; | 712 | props.max_brightness = ADP8860_MAX_BRIGHTNESS; |
714 | 713 | ||
715 | mutex_init(&data->lock); | 714 | mutex_init(&data->lock); |
716 | 715 | ||
717 | bl = backlight_device_register(dev_driver_string(&client->dev), | 716 | bl = backlight_device_register(dev_driver_string(&client->dev), |
718 | &client->dev, data, &adp8860_bl_ops, &props); | 717 | &client->dev, data, &adp8860_bl_ops, &props); |
719 | if (IS_ERR(bl)) { | 718 | if (IS_ERR(bl)) { |
720 | dev_err(&client->dev, "failed to register backlight\n"); | 719 | dev_err(&client->dev, "failed to register backlight\n"); |
721 | ret = PTR_ERR(bl); | 720 | ret = PTR_ERR(bl); |
722 | goto out2; | 721 | goto out2; |
723 | } | 722 | } |
724 | 723 | ||
725 | bl->props.max_brightness = | 724 | bl->props.max_brightness = |
726 | bl->props.brightness = ADP8860_MAX_BRIGHTNESS; | 725 | bl->props.brightness = ADP8860_MAX_BRIGHTNESS; |
727 | 726 | ||
728 | data->bl = bl; | 727 | data->bl = bl; |
729 | 728 | ||
730 | if (data->en_ambl_sens) | 729 | if (data->en_ambl_sens) |
731 | ret = sysfs_create_group(&bl->dev.kobj, | 730 | ret = sysfs_create_group(&bl->dev.kobj, |
732 | &adp8860_bl_attr_group); | 731 | &adp8860_bl_attr_group); |
733 | 732 | ||
734 | if (ret) { | 733 | if (ret) { |
735 | dev_err(&client->dev, "failed to register sysfs\n"); | 734 | dev_err(&client->dev, "failed to register sysfs\n"); |
736 | goto out1; | 735 | goto out1; |
737 | } | 736 | } |
738 | 737 | ||
739 | ret = adp8860_bl_setup(bl); | 738 | ret = adp8860_bl_setup(bl); |
740 | if (ret) { | 739 | if (ret) { |
741 | ret = -EIO; | 740 | ret = -EIO; |
742 | goto out; | 741 | goto out; |
743 | } | 742 | } |
744 | 743 | ||
745 | backlight_update_status(bl); | 744 | backlight_update_status(bl); |
746 | 745 | ||
747 | dev_info(&client->dev, "%s Rev.%d Backlight\n", | 746 | dev_info(&client->dev, "%s Rev.%d Backlight\n", |
748 | client->name, data->revid); | 747 | client->name, data->revid); |
749 | 748 | ||
750 | if (pdata->num_leds) | 749 | if (pdata->num_leds) |
751 | adp8860_led_probe(client); | 750 | adp8860_led_probe(client); |
752 | 751 | ||
753 | return 0; | 752 | return 0; |
754 | 753 | ||
755 | out: | 754 | out: |
756 | if (data->en_ambl_sens) | 755 | if (data->en_ambl_sens) |
757 | sysfs_remove_group(&data->bl->dev.kobj, | 756 | sysfs_remove_group(&data->bl->dev.kobj, |
758 | &adp8860_bl_attr_group); | 757 | &adp8860_bl_attr_group); |
759 | out1: | 758 | out1: |
760 | backlight_device_unregister(bl); | 759 | backlight_device_unregister(bl); |
761 | out2: | 760 | out2: |
762 | kfree(data); | 761 | kfree(data); |
763 | 762 | ||
764 | return ret; | 763 | return ret; |
765 | } | 764 | } |
766 | 765 | ||
767 | static int __devexit adp8860_remove(struct i2c_client *client) | 766 | static int __devexit adp8860_remove(struct i2c_client *client) |
768 | { | 767 | { |
769 | struct adp8860_bl *data = i2c_get_clientdata(client); | 768 | struct adp8860_bl *data = i2c_get_clientdata(client); |
770 | 769 | ||
771 | adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); | 770 | adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); |
772 | 771 | ||
773 | if (data->led) | 772 | if (data->led) |
774 | adp8860_led_remove(client); | 773 | adp8860_led_remove(client); |
775 | 774 | ||
776 | if (data->en_ambl_sens) | 775 | if (data->en_ambl_sens) |
777 | sysfs_remove_group(&data->bl->dev.kobj, | 776 | sysfs_remove_group(&data->bl->dev.kobj, |
778 | &adp8860_bl_attr_group); | 777 | &adp8860_bl_attr_group); |
779 | 778 | ||
780 | backlight_device_unregister(data->bl); | 779 | backlight_device_unregister(data->bl); |
781 | kfree(data); | 780 | kfree(data); |
782 | 781 | ||
783 | return 0; | 782 | return 0; |
784 | } | 783 | } |
785 | 784 | ||
786 | #ifdef CONFIG_PM | 785 | #ifdef CONFIG_PM |
787 | static int adp8860_i2c_suspend(struct i2c_client *client, pm_message_t message) | 786 | static int adp8860_i2c_suspend(struct i2c_client *client, pm_message_t message) |
788 | { | 787 | { |
789 | adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); | 788 | adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); |
790 | 789 | ||
791 | return 0; | 790 | return 0; |
792 | } | 791 | } |
793 | 792 | ||
794 | static int adp8860_i2c_resume(struct i2c_client *client) | 793 | static int adp8860_i2c_resume(struct i2c_client *client) |
795 | { | 794 | { |
796 | adp8860_set_bits(client, ADP8860_MDCR, NSTBY); | 795 | adp8860_set_bits(client, ADP8860_MDCR, NSTBY); |
797 | 796 | ||
798 | return 0; | 797 | return 0; |
799 | } | 798 | } |
800 | #else | 799 | #else |
801 | #define adp8860_i2c_suspend NULL | 800 | #define adp8860_i2c_suspend NULL |
802 | #define adp8860_i2c_resume NULL | 801 | #define adp8860_i2c_resume NULL |
803 | #endif | 802 | #endif |
804 | 803 | ||
805 | static const struct i2c_device_id adp8860_id[] = { | 804 | static const struct i2c_device_id adp8860_id[] = { |
806 | { "adp8860", adp8860 }, | 805 | { "adp8860", adp8860 }, |
807 | { "adp8861", adp8861 }, | 806 | { "adp8861", adp8861 }, |
808 | { "adp8863", adp8863 }, | 807 | { "adp8863", adp8863 }, |
809 | { } | 808 | { } |
810 | }; | 809 | }; |
811 | MODULE_DEVICE_TABLE(i2c, adp8860_id); | 810 | MODULE_DEVICE_TABLE(i2c, adp8860_id); |
812 | 811 | ||
813 | static struct i2c_driver adp8860_driver = { | 812 | static struct i2c_driver adp8860_driver = { |
814 | .driver = { | 813 | .driver = { |
815 | .name = KBUILD_MODNAME, | 814 | .name = KBUILD_MODNAME, |
816 | }, | 815 | }, |
817 | .probe = adp8860_probe, | 816 | .probe = adp8860_probe, |
818 | .remove = __devexit_p(adp8860_remove), | 817 | .remove = __devexit_p(adp8860_remove), |
819 | .suspend = adp8860_i2c_suspend, | 818 | .suspend = adp8860_i2c_suspend, |
820 | .resume = adp8860_i2c_resume, | 819 | .resume = adp8860_i2c_resume, |
821 | .id_table = adp8860_id, | 820 | .id_table = adp8860_id, |
822 | }; | 821 | }; |
823 | 822 | ||
824 | static int __init adp8860_init(void) | 823 | static int __init adp8860_init(void) |
825 | { | 824 | { |
826 | return i2c_add_driver(&adp8860_driver); | 825 | return i2c_add_driver(&adp8860_driver); |
827 | } | 826 | } |
828 | module_init(adp8860_init); | 827 | module_init(adp8860_init); |
829 | 828 | ||
830 | static void __exit adp8860_exit(void) | 829 | static void __exit adp8860_exit(void) |
831 | { | 830 | { |
832 | i2c_del_driver(&adp8860_driver); | 831 | i2c_del_driver(&adp8860_driver); |
833 | } | 832 | } |
834 | module_exit(adp8860_exit); | 833 | module_exit(adp8860_exit); |
835 | 834 | ||
836 | MODULE_LICENSE("GPL v2"); | 835 | MODULE_LICENSE("GPL v2"); |
837 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | 836 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); |
838 | MODULE_DESCRIPTION("ADP8860 Backlight driver"); | 837 | MODULE_DESCRIPTION("ADP8860 Backlight driver"); |
839 | MODULE_ALIAS("i2c:adp8860-backlight"); | 838 | MODULE_ALIAS("i2c:adp8860-backlight"); |
840 | 839 |
drivers/video/backlight/adp8870_bl.c
1 | /* | 1 | /* |
2 | * Backlight driver for Analog Devices ADP8870 Backlight Devices | 2 | * Backlight driver for Analog Devices ADP8870 Backlight Devices |
3 | * | 3 | * |
4 | * Copyright 2009-2011 Analog Devices Inc. | 4 | * Copyright 2009-2011 Analog Devices Inc. |
5 | * | 5 | * |
6 | * Licensed under the GPL-2 or later. | 6 | * Licensed under the GPL-2 or later. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
10 | #include <linux/version.h> | ||
11 | #include <linux/init.h> | 10 | #include <linux/init.h> |
12 | #include <linux/errno.h> | 11 | #include <linux/errno.h> |
13 | #include <linux/pm.h> | 12 | #include <linux/pm.h> |
14 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
15 | #include <linux/i2c.h> | 14 | #include <linux/i2c.h> |
16 | #include <linux/fb.h> | 15 | #include <linux/fb.h> |
17 | #include <linux/backlight.h> | 16 | #include <linux/backlight.h> |
18 | #include <linux/leds.h> | 17 | #include <linux/leds.h> |
19 | #include <linux/workqueue.h> | 18 | #include <linux/workqueue.h> |
20 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
21 | 20 | ||
22 | #include <linux/i2c/adp8870.h> | 21 | #include <linux/i2c/adp8870.h> |
23 | #define ADP8870_EXT_FEATURES | 22 | #define ADP8870_EXT_FEATURES |
24 | #define ADP8870_USE_LEDS | 23 | #define ADP8870_USE_LEDS |
25 | 24 | ||
26 | 25 | ||
27 | #define ADP8870_MFDVID 0x00 /* Manufacturer and device ID */ | 26 | #define ADP8870_MFDVID 0x00 /* Manufacturer and device ID */ |
28 | #define ADP8870_MDCR 0x01 /* Device mode and status */ | 27 | #define ADP8870_MDCR 0x01 /* Device mode and status */ |
29 | #define ADP8870_INT_STAT 0x02 /* Interrupts status */ | 28 | #define ADP8870_INT_STAT 0x02 /* Interrupts status */ |
30 | #define ADP8870_INT_EN 0x03 /* Interrupts enable */ | 29 | #define ADP8870_INT_EN 0x03 /* Interrupts enable */ |
31 | #define ADP8870_CFGR 0x04 /* Configuration register */ | 30 | #define ADP8870_CFGR 0x04 /* Configuration register */ |
32 | #define ADP8870_BLSEL 0x05 /* Sink enable backlight or independent */ | 31 | #define ADP8870_BLSEL 0x05 /* Sink enable backlight or independent */ |
33 | #define ADP8870_PWMLED 0x06 /* PWM Enable Selection Register */ | 32 | #define ADP8870_PWMLED 0x06 /* PWM Enable Selection Register */ |
34 | #define ADP8870_BLOFF 0x07 /* Backlight off timeout */ | 33 | #define ADP8870_BLOFF 0x07 /* Backlight off timeout */ |
35 | #define ADP8870_BLDIM 0x08 /* Backlight dim timeout */ | 34 | #define ADP8870_BLDIM 0x08 /* Backlight dim timeout */ |
36 | #define ADP8870_BLFR 0x09 /* Backlight fade in and out rates */ | 35 | #define ADP8870_BLFR 0x09 /* Backlight fade in and out rates */ |
37 | #define ADP8870_BLMX1 0x0A /* Backlight (Brightness Level 1-daylight) maximum current */ | 36 | #define ADP8870_BLMX1 0x0A /* Backlight (Brightness Level 1-daylight) maximum current */ |
38 | #define ADP8870_BLDM1 0x0B /* Backlight (Brightness Level 1-daylight) dim current */ | 37 | #define ADP8870_BLDM1 0x0B /* Backlight (Brightness Level 1-daylight) dim current */ |
39 | #define ADP8870_BLMX2 0x0C /* Backlight (Brightness Level 2-bright) maximum current */ | 38 | #define ADP8870_BLMX2 0x0C /* Backlight (Brightness Level 2-bright) maximum current */ |
40 | #define ADP8870_BLDM2 0x0D /* Backlight (Brightness Level 2-bright) dim current */ | 39 | #define ADP8870_BLDM2 0x0D /* Backlight (Brightness Level 2-bright) dim current */ |
41 | #define ADP8870_BLMX3 0x0E /* Backlight (Brightness Level 3-office) maximum current */ | 40 | #define ADP8870_BLMX3 0x0E /* Backlight (Brightness Level 3-office) maximum current */ |
42 | #define ADP8870_BLDM3 0x0F /* Backlight (Brightness Level 3-office) dim current */ | 41 | #define ADP8870_BLDM3 0x0F /* Backlight (Brightness Level 3-office) dim current */ |
43 | #define ADP8870_BLMX4 0x10 /* Backlight (Brightness Level 4-indoor) maximum current */ | 42 | #define ADP8870_BLMX4 0x10 /* Backlight (Brightness Level 4-indoor) maximum current */ |
44 | #define ADP8870_BLDM4 0x11 /* Backlight (Brightness Level 4-indoor) dim current */ | 43 | #define ADP8870_BLDM4 0x11 /* Backlight (Brightness Level 4-indoor) dim current */ |
45 | #define ADP8870_BLMX5 0x12 /* Backlight (Brightness Level 5-dark) maximum current */ | 44 | #define ADP8870_BLMX5 0x12 /* Backlight (Brightness Level 5-dark) maximum current */ |
46 | #define ADP8870_BLDM5 0x13 /* Backlight (Brightness Level 5-dark) dim current */ | 45 | #define ADP8870_BLDM5 0x13 /* Backlight (Brightness Level 5-dark) dim current */ |
47 | #define ADP8870_ISCLAW 0x1A /* Independent sink current fade law register */ | 46 | #define ADP8870_ISCLAW 0x1A /* Independent sink current fade law register */ |
48 | #define ADP8870_ISCC 0x1B /* Independent sink current control register */ | 47 | #define ADP8870_ISCC 0x1B /* Independent sink current control register */ |
49 | #define ADP8870_ISCT1 0x1C /* Independent Sink Current Timer Register LED[7:5] */ | 48 | #define ADP8870_ISCT1 0x1C /* Independent Sink Current Timer Register LED[7:5] */ |
50 | #define ADP8870_ISCT2 0x1D /* Independent Sink Current Timer Register LED[4:1] */ | 49 | #define ADP8870_ISCT2 0x1D /* Independent Sink Current Timer Register LED[4:1] */ |
51 | #define ADP8870_ISCF 0x1E /* Independent sink current fade register */ | 50 | #define ADP8870_ISCF 0x1E /* Independent sink current fade register */ |
52 | #define ADP8870_ISC1 0x1F /* Independent Sink Current LED1 */ | 51 | #define ADP8870_ISC1 0x1F /* Independent Sink Current LED1 */ |
53 | #define ADP8870_ISC2 0x20 /* Independent Sink Current LED2 */ | 52 | #define ADP8870_ISC2 0x20 /* Independent Sink Current LED2 */ |
54 | #define ADP8870_ISC3 0x21 /* Independent Sink Current LED3 */ | 53 | #define ADP8870_ISC3 0x21 /* Independent Sink Current LED3 */ |
55 | #define ADP8870_ISC4 0x22 /* Independent Sink Current LED4 */ | 54 | #define ADP8870_ISC4 0x22 /* Independent Sink Current LED4 */ |
56 | #define ADP8870_ISC5 0x23 /* Independent Sink Current LED5 */ | 55 | #define ADP8870_ISC5 0x23 /* Independent Sink Current LED5 */ |
57 | #define ADP8870_ISC6 0x24 /* Independent Sink Current LED6 */ | 56 | #define ADP8870_ISC6 0x24 /* Independent Sink Current LED6 */ |
58 | #define ADP8870_ISC7 0x25 /* Independent Sink Current LED7 (Brightness Level 1-daylight) */ | 57 | #define ADP8870_ISC7 0x25 /* Independent Sink Current LED7 (Brightness Level 1-daylight) */ |
59 | #define ADP8870_ISC7_L2 0x26 /* Independent Sink Current LED7 (Brightness Level 2-bright) */ | 58 | #define ADP8870_ISC7_L2 0x26 /* Independent Sink Current LED7 (Brightness Level 2-bright) */ |
60 | #define ADP8870_ISC7_L3 0x27 /* Independent Sink Current LED7 (Brightness Level 3-office) */ | 59 | #define ADP8870_ISC7_L3 0x27 /* Independent Sink Current LED7 (Brightness Level 3-office) */ |
61 | #define ADP8870_ISC7_L4 0x28 /* Independent Sink Current LED7 (Brightness Level 4-indoor) */ | 60 | #define ADP8870_ISC7_L4 0x28 /* Independent Sink Current LED7 (Brightness Level 4-indoor) */ |
62 | #define ADP8870_ISC7_L5 0x29 /* Independent Sink Current LED7 (Brightness Level 5-dark) */ | 61 | #define ADP8870_ISC7_L5 0x29 /* Independent Sink Current LED7 (Brightness Level 5-dark) */ |
63 | #define ADP8870_CMP_CTL 0x2D /* ALS Comparator Control Register */ | 62 | #define ADP8870_CMP_CTL 0x2D /* ALS Comparator Control Register */ |
64 | #define ADP8870_ALS1_EN 0x2E /* Main ALS comparator level enable */ | 63 | #define ADP8870_ALS1_EN 0x2E /* Main ALS comparator level enable */ |
65 | #define ADP8870_ALS2_EN 0x2F /* Second ALS comparator level enable */ | 64 | #define ADP8870_ALS2_EN 0x2F /* Second ALS comparator level enable */ |
66 | #define ADP8870_ALS1_STAT 0x30 /* Main ALS Comparator Status Register */ | 65 | #define ADP8870_ALS1_STAT 0x30 /* Main ALS Comparator Status Register */ |
67 | #define ADP8870_ALS2_STAT 0x31 /* Second ALS Comparator Status Register */ | 66 | #define ADP8870_ALS2_STAT 0x31 /* Second ALS Comparator Status Register */ |
68 | #define ADP8870_L2TRP 0x32 /* L2 comparator reference */ | 67 | #define ADP8870_L2TRP 0x32 /* L2 comparator reference */ |
69 | #define ADP8870_L2HYS 0x33 /* L2 hysteresis */ | 68 | #define ADP8870_L2HYS 0x33 /* L2 hysteresis */ |
70 | #define ADP8870_L3TRP 0x34 /* L3 comparator reference */ | 69 | #define ADP8870_L3TRP 0x34 /* L3 comparator reference */ |
71 | #define ADP8870_L3HYS 0x35 /* L3 hysteresis */ | 70 | #define ADP8870_L3HYS 0x35 /* L3 hysteresis */ |
72 | #define ADP8870_L4TRP 0x36 /* L4 comparator reference */ | 71 | #define ADP8870_L4TRP 0x36 /* L4 comparator reference */ |
73 | #define ADP8870_L4HYS 0x37 /* L4 hysteresis */ | 72 | #define ADP8870_L4HYS 0x37 /* L4 hysteresis */ |
74 | #define ADP8870_L5TRP 0x38 /* L5 comparator reference */ | 73 | #define ADP8870_L5TRP 0x38 /* L5 comparator reference */ |
75 | #define ADP8870_L5HYS 0x39 /* L5 hysteresis */ | 74 | #define ADP8870_L5HYS 0x39 /* L5 hysteresis */ |
76 | #define ADP8870_PH1LEVL 0x40 /* First phototransistor ambient light level-low byte register */ | 75 | #define ADP8870_PH1LEVL 0x40 /* First phototransistor ambient light level-low byte register */ |
77 | #define ADP8870_PH1LEVH 0x41 /* First phototransistor ambient light level-high byte register */ | 76 | #define ADP8870_PH1LEVH 0x41 /* First phototransistor ambient light level-high byte register */ |
78 | #define ADP8870_PH2LEVL 0x42 /* Second phototransistor ambient light level-low byte register */ | 77 | #define ADP8870_PH2LEVL 0x42 /* Second phototransistor ambient light level-low byte register */ |
79 | #define ADP8870_PH2LEVH 0x43 /* Second phototransistor ambient light level-high byte register */ | 78 | #define ADP8870_PH2LEVH 0x43 /* Second phototransistor ambient light level-high byte register */ |
80 | 79 | ||
81 | #define ADP8870_MANUFID 0x3 /* Analog Devices AD8870 Manufacturer and device ID */ | 80 | #define ADP8870_MANUFID 0x3 /* Analog Devices AD8870 Manufacturer and device ID */ |
82 | #define ADP8870_DEVID(x) ((x) & 0xF) | 81 | #define ADP8870_DEVID(x) ((x) & 0xF) |
83 | #define ADP8870_MANID(x) ((x) >> 4) | 82 | #define ADP8870_MANID(x) ((x) >> 4) |
84 | 83 | ||
85 | /* MDCR Device mode and status */ | 84 | /* MDCR Device mode and status */ |
86 | #define D7ALSEN (1 << 7) | 85 | #define D7ALSEN (1 << 7) |
87 | #define INT_CFG (1 << 6) | 86 | #define INT_CFG (1 << 6) |
88 | #define NSTBY (1 << 5) | 87 | #define NSTBY (1 << 5) |
89 | #define DIM_EN (1 << 4) | 88 | #define DIM_EN (1 << 4) |
90 | #define GDWN_DIS (1 << 3) | 89 | #define GDWN_DIS (1 << 3) |
91 | #define SIS_EN (1 << 2) | 90 | #define SIS_EN (1 << 2) |
92 | #define CMP_AUTOEN (1 << 1) | 91 | #define CMP_AUTOEN (1 << 1) |
93 | #define BLEN (1 << 0) | 92 | #define BLEN (1 << 0) |
94 | 93 | ||
95 | /* ADP8870_ALS1_EN Main ALS comparator level enable */ | 94 | /* ADP8870_ALS1_EN Main ALS comparator level enable */ |
96 | #define L5_EN (1 << 3) | 95 | #define L5_EN (1 << 3) |
97 | #define L4_EN (1 << 2) | 96 | #define L4_EN (1 << 2) |
98 | #define L3_EN (1 << 1) | 97 | #define L3_EN (1 << 1) |
99 | #define L2_EN (1 << 0) | 98 | #define L2_EN (1 << 0) |
100 | 99 | ||
101 | #define CFGR_BLV_SHIFT 3 | 100 | #define CFGR_BLV_SHIFT 3 |
102 | #define CFGR_BLV_MASK 0x7 | 101 | #define CFGR_BLV_MASK 0x7 |
103 | #define ADP8870_FLAG_LED_MASK 0xFF | 102 | #define ADP8870_FLAG_LED_MASK 0xFF |
104 | 103 | ||
105 | #define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4)) | 104 | #define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4)) |
106 | #define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1)) | 105 | #define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1)) |
107 | #define ALS_CMPR_CFG_VAL(filt) ((0x7 & (filt)) << 1) | 106 | #define ALS_CMPR_CFG_VAL(filt) ((0x7 & (filt)) << 1) |
108 | 107 | ||
109 | struct adp8870_bl { | 108 | struct adp8870_bl { |
110 | struct i2c_client *client; | 109 | struct i2c_client *client; |
111 | struct backlight_device *bl; | 110 | struct backlight_device *bl; |
112 | struct adp8870_led *led; | 111 | struct adp8870_led *led; |
113 | struct adp8870_backlight_platform_data *pdata; | 112 | struct adp8870_backlight_platform_data *pdata; |
114 | struct mutex lock; | 113 | struct mutex lock; |
115 | unsigned long cached_daylight_max; | 114 | unsigned long cached_daylight_max; |
116 | int id; | 115 | int id; |
117 | int revid; | 116 | int revid; |
118 | int current_brightness; | 117 | int current_brightness; |
119 | }; | 118 | }; |
120 | 119 | ||
121 | struct adp8870_led { | 120 | struct adp8870_led { |
122 | struct led_classdev cdev; | 121 | struct led_classdev cdev; |
123 | struct work_struct work; | 122 | struct work_struct work; |
124 | struct i2c_client *client; | 123 | struct i2c_client *client; |
125 | enum led_brightness new_brightness; | 124 | enum led_brightness new_brightness; |
126 | int id; | 125 | int id; |
127 | int flags; | 126 | int flags; |
128 | }; | 127 | }; |
129 | 128 | ||
130 | static int adp8870_read(struct i2c_client *client, int reg, uint8_t *val) | 129 | static int adp8870_read(struct i2c_client *client, int reg, uint8_t *val) |
131 | { | 130 | { |
132 | int ret; | 131 | int ret; |
133 | 132 | ||
134 | ret = i2c_smbus_read_byte_data(client, reg); | 133 | ret = i2c_smbus_read_byte_data(client, reg); |
135 | if (ret < 0) { | 134 | if (ret < 0) { |
136 | dev_err(&client->dev, "failed reading at 0x%02x\n", reg); | 135 | dev_err(&client->dev, "failed reading at 0x%02x\n", reg); |
137 | return ret; | 136 | return ret; |
138 | } | 137 | } |
139 | 138 | ||
140 | *val = ret; | 139 | *val = ret; |
141 | return 0; | 140 | return 0; |
142 | } | 141 | } |
143 | 142 | ||
144 | 143 | ||
145 | static int adp8870_write(struct i2c_client *client, u8 reg, u8 val) | 144 | static int adp8870_write(struct i2c_client *client, u8 reg, u8 val) |
146 | { | 145 | { |
147 | int ret = i2c_smbus_write_byte_data(client, reg, val); | 146 | int ret = i2c_smbus_write_byte_data(client, reg, val); |
148 | if (ret) | 147 | if (ret) |
149 | dev_err(&client->dev, "failed to write\n"); | 148 | dev_err(&client->dev, "failed to write\n"); |
150 | 149 | ||
151 | return ret; | 150 | return ret; |
152 | } | 151 | } |
153 | 152 | ||
154 | static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask) | 153 | static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask) |
155 | { | 154 | { |
156 | struct adp8870_bl *data = i2c_get_clientdata(client); | 155 | struct adp8870_bl *data = i2c_get_clientdata(client); |
157 | uint8_t reg_val; | 156 | uint8_t reg_val; |
158 | int ret; | 157 | int ret; |
159 | 158 | ||
160 | mutex_lock(&data->lock); | 159 | mutex_lock(&data->lock); |
161 | 160 | ||
162 | ret = adp8870_read(client, reg, ®_val); | 161 | ret = adp8870_read(client, reg, ®_val); |
163 | 162 | ||
164 | if (!ret && ((reg_val & bit_mask) == 0)) { | 163 | if (!ret && ((reg_val & bit_mask) == 0)) { |
165 | reg_val |= bit_mask; | 164 | reg_val |= bit_mask; |
166 | ret = adp8870_write(client, reg, reg_val); | 165 | ret = adp8870_write(client, reg, reg_val); |
167 | } | 166 | } |
168 | 167 | ||
169 | mutex_unlock(&data->lock); | 168 | mutex_unlock(&data->lock); |
170 | return ret; | 169 | return ret; |
171 | } | 170 | } |
172 | 171 | ||
173 | static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask) | 172 | static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask) |
174 | { | 173 | { |
175 | struct adp8870_bl *data = i2c_get_clientdata(client); | 174 | struct adp8870_bl *data = i2c_get_clientdata(client); |
176 | uint8_t reg_val; | 175 | uint8_t reg_val; |
177 | int ret; | 176 | int ret; |
178 | 177 | ||
179 | mutex_lock(&data->lock); | 178 | mutex_lock(&data->lock); |
180 | 179 | ||
181 | ret = adp8870_read(client, reg, ®_val); | 180 | ret = adp8870_read(client, reg, ®_val); |
182 | 181 | ||
183 | if (!ret && (reg_val & bit_mask)) { | 182 | if (!ret && (reg_val & bit_mask)) { |
184 | reg_val &= ~bit_mask; | 183 | reg_val &= ~bit_mask; |
185 | ret = adp8870_write(client, reg, reg_val); | 184 | ret = adp8870_write(client, reg, reg_val); |
186 | } | 185 | } |
187 | 186 | ||
188 | mutex_unlock(&data->lock); | 187 | mutex_unlock(&data->lock); |
189 | return ret; | 188 | return ret; |
190 | } | 189 | } |
191 | 190 | ||
192 | /* | 191 | /* |
193 | * Independent sink / LED | 192 | * Independent sink / LED |
194 | */ | 193 | */ |
195 | #if defined(ADP8870_USE_LEDS) | 194 | #if defined(ADP8870_USE_LEDS) |
196 | static void adp8870_led_work(struct work_struct *work) | 195 | static void adp8870_led_work(struct work_struct *work) |
197 | { | 196 | { |
198 | struct adp8870_led *led = container_of(work, struct adp8870_led, work); | 197 | struct adp8870_led *led = container_of(work, struct adp8870_led, work); |
199 | adp8870_write(led->client, ADP8870_ISC1 + led->id - 1, | 198 | adp8870_write(led->client, ADP8870_ISC1 + led->id - 1, |
200 | led->new_brightness >> 1); | 199 | led->new_brightness >> 1); |
201 | } | 200 | } |
202 | 201 | ||
203 | static void adp8870_led_set(struct led_classdev *led_cdev, | 202 | static void adp8870_led_set(struct led_classdev *led_cdev, |
204 | enum led_brightness value) | 203 | enum led_brightness value) |
205 | { | 204 | { |
206 | struct adp8870_led *led; | 205 | struct adp8870_led *led; |
207 | 206 | ||
208 | led = container_of(led_cdev, struct adp8870_led, cdev); | 207 | led = container_of(led_cdev, struct adp8870_led, cdev); |
209 | led->new_brightness = value; | 208 | led->new_brightness = value; |
210 | /* | 209 | /* |
211 | * Use workqueue for IO since I2C operations can sleep. | 210 | * Use workqueue for IO since I2C operations can sleep. |
212 | */ | 211 | */ |
213 | schedule_work(&led->work); | 212 | schedule_work(&led->work); |
214 | } | 213 | } |
215 | 214 | ||
216 | static int adp8870_led_setup(struct adp8870_led *led) | 215 | static int adp8870_led_setup(struct adp8870_led *led) |
217 | { | 216 | { |
218 | struct i2c_client *client = led->client; | 217 | struct i2c_client *client = led->client; |
219 | int ret = 0; | 218 | int ret = 0; |
220 | 219 | ||
221 | ret = adp8870_write(client, ADP8870_ISC1 + led->id - 1, 0); | 220 | ret = adp8870_write(client, ADP8870_ISC1 + led->id - 1, 0); |
222 | if (ret) | 221 | if (ret) |
223 | return ret; | 222 | return ret; |
224 | 223 | ||
225 | ret = adp8870_set_bits(client, ADP8870_ISCC, 1 << (led->id - 1)); | 224 | ret = adp8870_set_bits(client, ADP8870_ISCC, 1 << (led->id - 1)); |
226 | if (ret) | 225 | if (ret) |
227 | return ret; | 226 | return ret; |
228 | 227 | ||
229 | if (led->id > 4) | 228 | if (led->id > 4) |
230 | ret = adp8870_set_bits(client, ADP8870_ISCT1, | 229 | ret = adp8870_set_bits(client, ADP8870_ISCT1, |
231 | (led->flags & 0x3) << ((led->id - 5) * 2)); | 230 | (led->flags & 0x3) << ((led->id - 5) * 2)); |
232 | else | 231 | else |
233 | ret = adp8870_set_bits(client, ADP8870_ISCT2, | 232 | ret = adp8870_set_bits(client, ADP8870_ISCT2, |
234 | (led->flags & 0x3) << ((led->id - 1) * 2)); | 233 | (led->flags & 0x3) << ((led->id - 1) * 2)); |
235 | 234 | ||
236 | return ret; | 235 | return ret; |
237 | } | 236 | } |
238 | 237 | ||
239 | static int __devinit adp8870_led_probe(struct i2c_client *client) | 238 | static int __devinit adp8870_led_probe(struct i2c_client *client) |
240 | { | 239 | { |
241 | struct adp8870_backlight_platform_data *pdata = | 240 | struct adp8870_backlight_platform_data *pdata = |
242 | client->dev.platform_data; | 241 | client->dev.platform_data; |
243 | struct adp8870_bl *data = i2c_get_clientdata(client); | 242 | struct adp8870_bl *data = i2c_get_clientdata(client); |
244 | struct adp8870_led *led, *led_dat; | 243 | struct adp8870_led *led, *led_dat; |
245 | struct led_info *cur_led; | 244 | struct led_info *cur_led; |
246 | int ret, i; | 245 | int ret, i; |
247 | 246 | ||
248 | 247 | ||
249 | led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL); | 248 | led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL); |
250 | if (led == NULL) { | 249 | if (led == NULL) { |
251 | dev_err(&client->dev, "failed to alloc memory\n"); | 250 | dev_err(&client->dev, "failed to alloc memory\n"); |
252 | return -ENOMEM; | 251 | return -ENOMEM; |
253 | } | 252 | } |
254 | 253 | ||
255 | ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law); | 254 | ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law); |
256 | if (ret) | 255 | if (ret) |
257 | goto err_free; | 256 | goto err_free; |
258 | 257 | ||
259 | ret = adp8870_write(client, ADP8870_ISCT1, | 258 | ret = adp8870_write(client, ADP8870_ISCT1, |
260 | (pdata->led_on_time & 0x3) << 6); | 259 | (pdata->led_on_time & 0x3) << 6); |
261 | if (ret) | 260 | if (ret) |
262 | goto err_free; | 261 | goto err_free; |
263 | 262 | ||
264 | ret = adp8870_write(client, ADP8870_ISCF, | 263 | ret = adp8870_write(client, ADP8870_ISCF, |
265 | FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); | 264 | FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); |
266 | if (ret) | 265 | if (ret) |
267 | goto err_free; | 266 | goto err_free; |
268 | 267 | ||
269 | for (i = 0; i < pdata->num_leds; ++i) { | 268 | for (i = 0; i < pdata->num_leds; ++i) { |
270 | cur_led = &pdata->leds[i]; | 269 | cur_led = &pdata->leds[i]; |
271 | led_dat = &led[i]; | 270 | led_dat = &led[i]; |
272 | 271 | ||
273 | led_dat->id = cur_led->flags & ADP8870_FLAG_LED_MASK; | 272 | led_dat->id = cur_led->flags & ADP8870_FLAG_LED_MASK; |
274 | 273 | ||
275 | if (led_dat->id > 7 || led_dat->id < 1) { | 274 | if (led_dat->id > 7 || led_dat->id < 1) { |
276 | dev_err(&client->dev, "Invalid LED ID %d\n", | 275 | dev_err(&client->dev, "Invalid LED ID %d\n", |
277 | led_dat->id); | 276 | led_dat->id); |
278 | goto err; | 277 | goto err; |
279 | } | 278 | } |
280 | 279 | ||
281 | if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) { | 280 | if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) { |
282 | dev_err(&client->dev, "LED %d used by Backlight\n", | 281 | dev_err(&client->dev, "LED %d used by Backlight\n", |
283 | led_dat->id); | 282 | led_dat->id); |
284 | goto err; | 283 | goto err; |
285 | } | 284 | } |
286 | 285 | ||
287 | led_dat->cdev.name = cur_led->name; | 286 | led_dat->cdev.name = cur_led->name; |
288 | led_dat->cdev.default_trigger = cur_led->default_trigger; | 287 | led_dat->cdev.default_trigger = cur_led->default_trigger; |
289 | led_dat->cdev.brightness_set = adp8870_led_set; | 288 | led_dat->cdev.brightness_set = adp8870_led_set; |
290 | led_dat->cdev.brightness = LED_OFF; | 289 | led_dat->cdev.brightness = LED_OFF; |
291 | led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; | 290 | led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; |
292 | led_dat->client = client; | 291 | led_dat->client = client; |
293 | led_dat->new_brightness = LED_OFF; | 292 | led_dat->new_brightness = LED_OFF; |
294 | INIT_WORK(&led_dat->work, adp8870_led_work); | 293 | INIT_WORK(&led_dat->work, adp8870_led_work); |
295 | 294 | ||
296 | ret = led_classdev_register(&client->dev, &led_dat->cdev); | 295 | ret = led_classdev_register(&client->dev, &led_dat->cdev); |
297 | if (ret) { | 296 | if (ret) { |
298 | dev_err(&client->dev, "failed to register LED %d\n", | 297 | dev_err(&client->dev, "failed to register LED %d\n", |
299 | led_dat->id); | 298 | led_dat->id); |
300 | goto err; | 299 | goto err; |
301 | } | 300 | } |
302 | 301 | ||
303 | ret = adp8870_led_setup(led_dat); | 302 | ret = adp8870_led_setup(led_dat); |
304 | if (ret) { | 303 | if (ret) { |
305 | dev_err(&client->dev, "failed to write\n"); | 304 | dev_err(&client->dev, "failed to write\n"); |
306 | i++; | 305 | i++; |
307 | goto err; | 306 | goto err; |
308 | } | 307 | } |
309 | } | 308 | } |
310 | 309 | ||
311 | data->led = led; | 310 | data->led = led; |
312 | 311 | ||
313 | return 0; | 312 | return 0; |
314 | 313 | ||
315 | err: | 314 | err: |
316 | for (i = i - 1; i >= 0; --i) { | 315 | for (i = i - 1; i >= 0; --i) { |
317 | led_classdev_unregister(&led[i].cdev); | 316 | led_classdev_unregister(&led[i].cdev); |
318 | cancel_work_sync(&led[i].work); | 317 | cancel_work_sync(&led[i].work); |
319 | } | 318 | } |
320 | 319 | ||
321 | err_free: | 320 | err_free: |
322 | kfree(led); | 321 | kfree(led); |
323 | 322 | ||
324 | return ret; | 323 | return ret; |
325 | } | 324 | } |
326 | 325 | ||
327 | static int __devexit adp8870_led_remove(struct i2c_client *client) | 326 | static int __devexit adp8870_led_remove(struct i2c_client *client) |
328 | { | 327 | { |
329 | struct adp8870_backlight_platform_data *pdata = | 328 | struct adp8870_backlight_platform_data *pdata = |
330 | client->dev.platform_data; | 329 | client->dev.platform_data; |
331 | struct adp8870_bl *data = i2c_get_clientdata(client); | 330 | struct adp8870_bl *data = i2c_get_clientdata(client); |
332 | int i; | 331 | int i; |
333 | 332 | ||
334 | for (i = 0; i < pdata->num_leds; i++) { | 333 | for (i = 0; i < pdata->num_leds; i++) { |
335 | led_classdev_unregister(&data->led[i].cdev); | 334 | led_classdev_unregister(&data->led[i].cdev); |
336 | cancel_work_sync(&data->led[i].work); | 335 | cancel_work_sync(&data->led[i].work); |
337 | } | 336 | } |
338 | 337 | ||
339 | kfree(data->led); | 338 | kfree(data->led); |
340 | return 0; | 339 | return 0; |
341 | } | 340 | } |
342 | #else | 341 | #else |
343 | static int __devinit adp8870_led_probe(struct i2c_client *client) | 342 | static int __devinit adp8870_led_probe(struct i2c_client *client) |
344 | { | 343 | { |
345 | return 0; | 344 | return 0; |
346 | } | 345 | } |
347 | 346 | ||
348 | static int __devexit adp8870_led_remove(struct i2c_client *client) | 347 | static int __devexit adp8870_led_remove(struct i2c_client *client) |
349 | { | 348 | { |
350 | return 0; | 349 | return 0; |
351 | } | 350 | } |
352 | #endif | 351 | #endif |
353 | 352 | ||
354 | static int adp8870_bl_set(struct backlight_device *bl, int brightness) | 353 | static int adp8870_bl_set(struct backlight_device *bl, int brightness) |
355 | { | 354 | { |
356 | struct adp8870_bl *data = bl_get_data(bl); | 355 | struct adp8870_bl *data = bl_get_data(bl); |
357 | struct i2c_client *client = data->client; | 356 | struct i2c_client *client = data->client; |
358 | int ret = 0; | 357 | int ret = 0; |
359 | 358 | ||
360 | if (data->pdata->en_ambl_sens) { | 359 | if (data->pdata->en_ambl_sens) { |
361 | if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) { | 360 | if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) { |
362 | /* Disable Ambient Light auto adjust */ | 361 | /* Disable Ambient Light auto adjust */ |
363 | ret = adp8870_clr_bits(client, ADP8870_MDCR, | 362 | ret = adp8870_clr_bits(client, ADP8870_MDCR, |
364 | CMP_AUTOEN); | 363 | CMP_AUTOEN); |
365 | if (ret) | 364 | if (ret) |
366 | return ret; | 365 | return ret; |
367 | ret = adp8870_write(client, ADP8870_BLMX1, brightness); | 366 | ret = adp8870_write(client, ADP8870_BLMX1, brightness); |
368 | if (ret) | 367 | if (ret) |
369 | return ret; | 368 | return ret; |
370 | } else { | 369 | } else { |
371 | /* | 370 | /* |
372 | * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust | 371 | * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust |
373 | * restore daylight l1 sysfs brightness | 372 | * restore daylight l1 sysfs brightness |
374 | */ | 373 | */ |
375 | ret = adp8870_write(client, ADP8870_BLMX1, | 374 | ret = adp8870_write(client, ADP8870_BLMX1, |
376 | data->cached_daylight_max); | 375 | data->cached_daylight_max); |
377 | if (ret) | 376 | if (ret) |
378 | return ret; | 377 | return ret; |
379 | 378 | ||
380 | ret = adp8870_set_bits(client, ADP8870_MDCR, | 379 | ret = adp8870_set_bits(client, ADP8870_MDCR, |
381 | CMP_AUTOEN); | 380 | CMP_AUTOEN); |
382 | if (ret) | 381 | if (ret) |
383 | return ret; | 382 | return ret; |
384 | } | 383 | } |
385 | } else { | 384 | } else { |
386 | ret = adp8870_write(client, ADP8870_BLMX1, brightness); | 385 | ret = adp8870_write(client, ADP8870_BLMX1, brightness); |
387 | if (ret) | 386 | if (ret) |
388 | return ret; | 387 | return ret; |
389 | } | 388 | } |
390 | 389 | ||
391 | if (data->current_brightness && brightness == 0) | 390 | if (data->current_brightness && brightness == 0) |
392 | ret = adp8870_set_bits(client, | 391 | ret = adp8870_set_bits(client, |
393 | ADP8870_MDCR, DIM_EN); | 392 | ADP8870_MDCR, DIM_EN); |
394 | else if (data->current_brightness == 0 && brightness) | 393 | else if (data->current_brightness == 0 && brightness) |
395 | ret = adp8870_clr_bits(client, | 394 | ret = adp8870_clr_bits(client, |
396 | ADP8870_MDCR, DIM_EN); | 395 | ADP8870_MDCR, DIM_EN); |
397 | 396 | ||
398 | if (!ret) | 397 | if (!ret) |
399 | data->current_brightness = brightness; | 398 | data->current_brightness = brightness; |
400 | 399 | ||
401 | return ret; | 400 | return ret; |
402 | } | 401 | } |
403 | 402 | ||
404 | static int adp8870_bl_update_status(struct backlight_device *bl) | 403 | static int adp8870_bl_update_status(struct backlight_device *bl) |
405 | { | 404 | { |
406 | int brightness = bl->props.brightness; | 405 | int brightness = bl->props.brightness; |
407 | if (bl->props.power != FB_BLANK_UNBLANK) | 406 | if (bl->props.power != FB_BLANK_UNBLANK) |
408 | brightness = 0; | 407 | brightness = 0; |
409 | 408 | ||
410 | if (bl->props.fb_blank != FB_BLANK_UNBLANK) | 409 | if (bl->props.fb_blank != FB_BLANK_UNBLANK) |
411 | brightness = 0; | 410 | brightness = 0; |
412 | 411 | ||
413 | return adp8870_bl_set(bl, brightness); | 412 | return adp8870_bl_set(bl, brightness); |
414 | } | 413 | } |
415 | 414 | ||
416 | static int adp8870_bl_get_brightness(struct backlight_device *bl) | 415 | static int adp8870_bl_get_brightness(struct backlight_device *bl) |
417 | { | 416 | { |
418 | struct adp8870_bl *data = bl_get_data(bl); | 417 | struct adp8870_bl *data = bl_get_data(bl); |
419 | 418 | ||
420 | return data->current_brightness; | 419 | return data->current_brightness; |
421 | } | 420 | } |
422 | 421 | ||
423 | static const struct backlight_ops adp8870_bl_ops = { | 422 | static const struct backlight_ops adp8870_bl_ops = { |
424 | .update_status = adp8870_bl_update_status, | 423 | .update_status = adp8870_bl_update_status, |
425 | .get_brightness = adp8870_bl_get_brightness, | 424 | .get_brightness = adp8870_bl_get_brightness, |
426 | }; | 425 | }; |
427 | 426 | ||
428 | static int adp8870_bl_setup(struct backlight_device *bl) | 427 | static int adp8870_bl_setup(struct backlight_device *bl) |
429 | { | 428 | { |
430 | struct adp8870_bl *data = bl_get_data(bl); | 429 | struct adp8870_bl *data = bl_get_data(bl); |
431 | struct i2c_client *client = data->client; | 430 | struct i2c_client *client = data->client; |
432 | struct adp8870_backlight_platform_data *pdata = data->pdata; | 431 | struct adp8870_backlight_platform_data *pdata = data->pdata; |
433 | int ret = 0; | 432 | int ret = 0; |
434 | 433 | ||
435 | ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign); | 434 | ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign); |
436 | if (ret) | 435 | if (ret) |
437 | return ret; | 436 | return ret; |
438 | 437 | ||
439 | ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign); | 438 | ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign); |
440 | if (ret) | 439 | if (ret) |
441 | return ret; | 440 | return ret; |
442 | 441 | ||
443 | ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max); | 442 | ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max); |
444 | if (ret) | 443 | if (ret) |
445 | return ret; | 444 | return ret; |
446 | 445 | ||
447 | ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim); | 446 | ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim); |
448 | if (ret) | 447 | if (ret) |
449 | return ret; | 448 | return ret; |
450 | 449 | ||
451 | if (pdata->en_ambl_sens) { | 450 | if (pdata->en_ambl_sens) { |
452 | data->cached_daylight_max = pdata->l1_daylight_max; | 451 | data->cached_daylight_max = pdata->l1_daylight_max; |
453 | ret = adp8870_write(client, ADP8870_BLMX2, | 452 | ret = adp8870_write(client, ADP8870_BLMX2, |
454 | pdata->l2_bright_max); | 453 | pdata->l2_bright_max); |
455 | if (ret) | 454 | if (ret) |
456 | return ret; | 455 | return ret; |
457 | ret = adp8870_write(client, ADP8870_BLDM2, | 456 | ret = adp8870_write(client, ADP8870_BLDM2, |
458 | pdata->l2_bright_dim); | 457 | pdata->l2_bright_dim); |
459 | if (ret) | 458 | if (ret) |
460 | return ret; | 459 | return ret; |
461 | 460 | ||
462 | ret = adp8870_write(client, ADP8870_BLMX3, | 461 | ret = adp8870_write(client, ADP8870_BLMX3, |
463 | pdata->l3_office_max); | 462 | pdata->l3_office_max); |
464 | if (ret) | 463 | if (ret) |
465 | return ret; | 464 | return ret; |
466 | ret = adp8870_write(client, ADP8870_BLDM3, | 465 | ret = adp8870_write(client, ADP8870_BLDM3, |
467 | pdata->l3_office_dim); | 466 | pdata->l3_office_dim); |
468 | if (ret) | 467 | if (ret) |
469 | return ret; | 468 | return ret; |
470 | 469 | ||
471 | ret = adp8870_write(client, ADP8870_BLMX4, | 470 | ret = adp8870_write(client, ADP8870_BLMX4, |
472 | pdata->l4_indoor_max); | 471 | pdata->l4_indoor_max); |
473 | if (ret) | 472 | if (ret) |
474 | return ret; | 473 | return ret; |
475 | 474 | ||
476 | ret = adp8870_write(client, ADP8870_BLDM4, | 475 | ret = adp8870_write(client, ADP8870_BLDM4, |
477 | pdata->l4_indor_dim); | 476 | pdata->l4_indor_dim); |
478 | if (ret) | 477 | if (ret) |
479 | return ret; | 478 | return ret; |
480 | 479 | ||
481 | ret = adp8870_write(client, ADP8870_BLMX5, | 480 | ret = adp8870_write(client, ADP8870_BLMX5, |
482 | pdata->l5_dark_max); | 481 | pdata->l5_dark_max); |
483 | if (ret) | 482 | if (ret) |
484 | return ret; | 483 | return ret; |
485 | 484 | ||
486 | ret = adp8870_write(client, ADP8870_BLDM5, | 485 | ret = adp8870_write(client, ADP8870_BLDM5, |
487 | pdata->l5_dark_dim); | 486 | pdata->l5_dark_dim); |
488 | if (ret) | 487 | if (ret) |
489 | return ret; | 488 | return ret; |
490 | 489 | ||
491 | ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip); | 490 | ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip); |
492 | if (ret) | 491 | if (ret) |
493 | return ret; | 492 | return ret; |
494 | 493 | ||
495 | ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst); | 494 | ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst); |
496 | if (ret) | 495 | if (ret) |
497 | return ret; | 496 | return ret; |
498 | 497 | ||
499 | ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip); | 498 | ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip); |
500 | if (ret) | 499 | if (ret) |
501 | return ret; | 500 | return ret; |
502 | 501 | ||
503 | ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst); | 502 | ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst); |
504 | if (ret) | 503 | if (ret) |
505 | return ret; | 504 | return ret; |
506 | 505 | ||
507 | ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip); | 506 | ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip); |
508 | if (ret) | 507 | if (ret) |
509 | return ret; | 508 | return ret; |
510 | 509 | ||
511 | ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst); | 510 | ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst); |
512 | if (ret) | 511 | if (ret) |
513 | return ret; | 512 | return ret; |
514 | 513 | ||
515 | ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip); | 514 | ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip); |
516 | if (ret) | 515 | if (ret) |
517 | return ret; | 516 | return ret; |
518 | 517 | ||
519 | ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst); | 518 | ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst); |
520 | if (ret) | 519 | if (ret) |
521 | return ret; | 520 | return ret; |
522 | 521 | ||
523 | ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN | | 522 | ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN | |
524 | L3_EN | L2_EN); | 523 | L3_EN | L2_EN); |
525 | if (ret) | 524 | if (ret) |
526 | return ret; | 525 | return ret; |
527 | 526 | ||
528 | ret = adp8870_write(client, ADP8870_CMP_CTL, | 527 | ret = adp8870_write(client, ADP8870_CMP_CTL, |
529 | ALS_CMPR_CFG_VAL(pdata->abml_filt)); | 528 | ALS_CMPR_CFG_VAL(pdata->abml_filt)); |
530 | if (ret) | 529 | if (ret) |
531 | return ret; | 530 | return ret; |
532 | } | 531 | } |
533 | 532 | ||
534 | ret = adp8870_write(client, ADP8870_CFGR, | 533 | ret = adp8870_write(client, ADP8870_CFGR, |
535 | BL_CFGR_VAL(pdata->bl_fade_law, 0)); | 534 | BL_CFGR_VAL(pdata->bl_fade_law, 0)); |
536 | if (ret) | 535 | if (ret) |
537 | return ret; | 536 | return ret; |
538 | 537 | ||
539 | ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in, | 538 | ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in, |
540 | pdata->bl_fade_out)); | 539 | pdata->bl_fade_out)); |
541 | if (ret) | 540 | if (ret) |
542 | return ret; | 541 | return ret; |
543 | /* | 542 | /* |
544 | * ADP8870 Rev0 requires GDWN_DIS bit set | 543 | * ADP8870 Rev0 requires GDWN_DIS bit set |
545 | */ | 544 | */ |
546 | 545 | ||
547 | ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY | | 546 | ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY | |
548 | (data->revid == 0 ? GDWN_DIS : 0)); | 547 | (data->revid == 0 ? GDWN_DIS : 0)); |
549 | 548 | ||
550 | return ret; | 549 | return ret; |
551 | } | 550 | } |
552 | 551 | ||
553 | static ssize_t adp8870_show(struct device *dev, char *buf, int reg) | 552 | static ssize_t adp8870_show(struct device *dev, char *buf, int reg) |
554 | { | 553 | { |
555 | struct adp8870_bl *data = dev_get_drvdata(dev); | 554 | struct adp8870_bl *data = dev_get_drvdata(dev); |
556 | int error; | 555 | int error; |
557 | uint8_t reg_val; | 556 | uint8_t reg_val; |
558 | 557 | ||
559 | mutex_lock(&data->lock); | 558 | mutex_lock(&data->lock); |
560 | error = adp8870_read(data->client, reg, ®_val); | 559 | error = adp8870_read(data->client, reg, ®_val); |
561 | mutex_unlock(&data->lock); | 560 | mutex_unlock(&data->lock); |
562 | 561 | ||
563 | if (error < 0) | 562 | if (error < 0) |
564 | return error; | 563 | return error; |
565 | 564 | ||
566 | return sprintf(buf, "%u\n", reg_val); | 565 | return sprintf(buf, "%u\n", reg_val); |
567 | } | 566 | } |
568 | 567 | ||
569 | static ssize_t adp8870_store(struct device *dev, const char *buf, | 568 | static ssize_t adp8870_store(struct device *dev, const char *buf, |
570 | size_t count, int reg) | 569 | size_t count, int reg) |
571 | { | 570 | { |
572 | struct adp8870_bl *data = dev_get_drvdata(dev); | 571 | struct adp8870_bl *data = dev_get_drvdata(dev); |
573 | unsigned long val; | 572 | unsigned long val; |
574 | int ret; | 573 | int ret; |
575 | 574 | ||
576 | ret = strict_strtoul(buf, 10, &val); | 575 | ret = strict_strtoul(buf, 10, &val); |
577 | if (ret) | 576 | if (ret) |
578 | return ret; | 577 | return ret; |
579 | 578 | ||
580 | mutex_lock(&data->lock); | 579 | mutex_lock(&data->lock); |
581 | adp8870_write(data->client, reg, val); | 580 | adp8870_write(data->client, reg, val); |
582 | mutex_unlock(&data->lock); | 581 | mutex_unlock(&data->lock); |
583 | 582 | ||
584 | return count; | 583 | return count; |
585 | } | 584 | } |
586 | 585 | ||
587 | static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev, | 586 | static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev, |
588 | struct device_attribute *attr, char *buf) | 587 | struct device_attribute *attr, char *buf) |
589 | { | 588 | { |
590 | return adp8870_show(dev, buf, ADP8870_BLMX5); | 589 | return adp8870_show(dev, buf, ADP8870_BLMX5); |
591 | } | 590 | } |
592 | 591 | ||
593 | static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev, | 592 | static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev, |
594 | struct device_attribute *attr, const char *buf, size_t count) | 593 | struct device_attribute *attr, const char *buf, size_t count) |
595 | { | 594 | { |
596 | return adp8870_store(dev, buf, count, ADP8870_BLMX5); | 595 | return adp8870_store(dev, buf, count, ADP8870_BLMX5); |
597 | } | 596 | } |
598 | static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show, | 597 | static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show, |
599 | adp8870_bl_l5_dark_max_store); | 598 | adp8870_bl_l5_dark_max_store); |
600 | 599 | ||
601 | 600 | ||
602 | static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev, | 601 | static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev, |
603 | struct device_attribute *attr, char *buf) | 602 | struct device_attribute *attr, char *buf) |
604 | { | 603 | { |
605 | return adp8870_show(dev, buf, ADP8870_BLMX4); | 604 | return adp8870_show(dev, buf, ADP8870_BLMX4); |
606 | } | 605 | } |
607 | 606 | ||
608 | static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev, | 607 | static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev, |
609 | struct device_attribute *attr, const char *buf, size_t count) | 608 | struct device_attribute *attr, const char *buf, size_t count) |
610 | { | 609 | { |
611 | return adp8870_store(dev, buf, count, ADP8870_BLMX4); | 610 | return adp8870_store(dev, buf, count, ADP8870_BLMX4); |
612 | } | 611 | } |
613 | static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show, | 612 | static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show, |
614 | adp8870_bl_l4_indoor_max_store); | 613 | adp8870_bl_l4_indoor_max_store); |
615 | 614 | ||
616 | 615 | ||
617 | static ssize_t adp8870_bl_l3_office_max_show(struct device *dev, | 616 | static ssize_t adp8870_bl_l3_office_max_show(struct device *dev, |
618 | struct device_attribute *attr, char *buf) | 617 | struct device_attribute *attr, char *buf) |
619 | { | 618 | { |
620 | return adp8870_show(dev, buf, ADP8870_BLMX3); | 619 | return adp8870_show(dev, buf, ADP8870_BLMX3); |
621 | } | 620 | } |
622 | 621 | ||
623 | static ssize_t adp8870_bl_l3_office_max_store(struct device *dev, | 622 | static ssize_t adp8870_bl_l3_office_max_store(struct device *dev, |
624 | struct device_attribute *attr, const char *buf, size_t count) | 623 | struct device_attribute *attr, const char *buf, size_t count) |
625 | { | 624 | { |
626 | return adp8870_store(dev, buf, count, ADP8870_BLMX3); | 625 | return adp8870_store(dev, buf, count, ADP8870_BLMX3); |
627 | } | 626 | } |
628 | 627 | ||
629 | static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show, | 628 | static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show, |
630 | adp8870_bl_l3_office_max_store); | 629 | adp8870_bl_l3_office_max_store); |
631 | 630 | ||
632 | static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev, | 631 | static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev, |
633 | struct device_attribute *attr, char *buf) | 632 | struct device_attribute *attr, char *buf) |
634 | { | 633 | { |
635 | return adp8870_show(dev, buf, ADP8870_BLMX2); | 634 | return adp8870_show(dev, buf, ADP8870_BLMX2); |
636 | } | 635 | } |
637 | 636 | ||
638 | static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev, | 637 | static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev, |
639 | struct device_attribute *attr, const char *buf, size_t count) | 638 | struct device_attribute *attr, const char *buf, size_t count) |
640 | { | 639 | { |
641 | return adp8870_store(dev, buf, count, ADP8870_BLMX2); | 640 | return adp8870_store(dev, buf, count, ADP8870_BLMX2); |
642 | } | 641 | } |
643 | static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show, | 642 | static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show, |
644 | adp8870_bl_l2_bright_max_store); | 643 | adp8870_bl_l2_bright_max_store); |
645 | 644 | ||
646 | static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev, | 645 | static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev, |
647 | struct device_attribute *attr, char *buf) | 646 | struct device_attribute *attr, char *buf) |
648 | { | 647 | { |
649 | return adp8870_show(dev, buf, ADP8870_BLMX1); | 648 | return adp8870_show(dev, buf, ADP8870_BLMX1); |
650 | } | 649 | } |
651 | 650 | ||
652 | static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev, | 651 | static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev, |
653 | struct device_attribute *attr, const char *buf, size_t count) | 652 | struct device_attribute *attr, const char *buf, size_t count) |
654 | { | 653 | { |
655 | struct adp8870_bl *data = dev_get_drvdata(dev); | 654 | struct adp8870_bl *data = dev_get_drvdata(dev); |
656 | int ret = strict_strtoul(buf, 10, &data->cached_daylight_max); | 655 | int ret = strict_strtoul(buf, 10, &data->cached_daylight_max); |
657 | if (ret) | 656 | if (ret) |
658 | return ret; | 657 | return ret; |
659 | 658 | ||
660 | return adp8870_store(dev, buf, count, ADP8870_BLMX1); | 659 | return adp8870_store(dev, buf, count, ADP8870_BLMX1); |
661 | } | 660 | } |
662 | static DEVICE_ATTR(l1_daylight_max, 0664, adp8870_bl_l1_daylight_max_show, | 661 | static DEVICE_ATTR(l1_daylight_max, 0664, adp8870_bl_l1_daylight_max_show, |
663 | adp8870_bl_l1_daylight_max_store); | 662 | adp8870_bl_l1_daylight_max_store); |
664 | 663 | ||
665 | static ssize_t adp8870_bl_l5_dark_dim_show(struct device *dev, | 664 | static ssize_t adp8870_bl_l5_dark_dim_show(struct device *dev, |
666 | struct device_attribute *attr, char *buf) | 665 | struct device_attribute *attr, char *buf) |
667 | { | 666 | { |
668 | return adp8870_show(dev, buf, ADP8870_BLDM5); | 667 | return adp8870_show(dev, buf, ADP8870_BLDM5); |
669 | } | 668 | } |
670 | 669 | ||
671 | static ssize_t adp8870_bl_l5_dark_dim_store(struct device *dev, | 670 | static ssize_t adp8870_bl_l5_dark_dim_store(struct device *dev, |
672 | struct device_attribute *attr, | 671 | struct device_attribute *attr, |
673 | const char *buf, size_t count) | 672 | const char *buf, size_t count) |
674 | { | 673 | { |
675 | return adp8870_store(dev, buf, count, ADP8870_BLDM5); | 674 | return adp8870_store(dev, buf, count, ADP8870_BLDM5); |
676 | } | 675 | } |
677 | static DEVICE_ATTR(l5_dark_dim, 0664, adp8870_bl_l5_dark_dim_show, | 676 | static DEVICE_ATTR(l5_dark_dim, 0664, adp8870_bl_l5_dark_dim_show, |
678 | adp8870_bl_l5_dark_dim_store); | 677 | adp8870_bl_l5_dark_dim_store); |
679 | 678 | ||
680 | static ssize_t adp8870_bl_l4_indoor_dim_show(struct device *dev, | 679 | static ssize_t adp8870_bl_l4_indoor_dim_show(struct device *dev, |
681 | struct device_attribute *attr, char *buf) | 680 | struct device_attribute *attr, char *buf) |
682 | { | 681 | { |
683 | return adp8870_show(dev, buf, ADP8870_BLDM4); | 682 | return adp8870_show(dev, buf, ADP8870_BLDM4); |
684 | } | 683 | } |
685 | 684 | ||
686 | static ssize_t adp8870_bl_l4_indoor_dim_store(struct device *dev, | 685 | static ssize_t adp8870_bl_l4_indoor_dim_store(struct device *dev, |
687 | struct device_attribute *attr, | 686 | struct device_attribute *attr, |
688 | const char *buf, size_t count) | 687 | const char *buf, size_t count) |
689 | { | 688 | { |
690 | return adp8870_store(dev, buf, count, ADP8870_BLDM4); | 689 | return adp8870_store(dev, buf, count, ADP8870_BLDM4); |
691 | } | 690 | } |
692 | static DEVICE_ATTR(l4_indoor_dim, 0664, adp8870_bl_l4_indoor_dim_show, | 691 | static DEVICE_ATTR(l4_indoor_dim, 0664, adp8870_bl_l4_indoor_dim_show, |
693 | adp8870_bl_l4_indoor_dim_store); | 692 | adp8870_bl_l4_indoor_dim_store); |
694 | 693 | ||
695 | 694 | ||
696 | static ssize_t adp8870_bl_l3_office_dim_show(struct device *dev, | 695 | static ssize_t adp8870_bl_l3_office_dim_show(struct device *dev, |
697 | struct device_attribute *attr, char *buf) | 696 | struct device_attribute *attr, char *buf) |
698 | { | 697 | { |
699 | return adp8870_show(dev, buf, ADP8870_BLDM3); | 698 | return adp8870_show(dev, buf, ADP8870_BLDM3); |
700 | } | 699 | } |
701 | 700 | ||
702 | static ssize_t adp8870_bl_l3_office_dim_store(struct device *dev, | 701 | static ssize_t adp8870_bl_l3_office_dim_store(struct device *dev, |
703 | struct device_attribute *attr, | 702 | struct device_attribute *attr, |
704 | const char *buf, size_t count) | 703 | const char *buf, size_t count) |
705 | { | 704 | { |
706 | return adp8870_store(dev, buf, count, ADP8870_BLDM3); | 705 | return adp8870_store(dev, buf, count, ADP8870_BLDM3); |
707 | } | 706 | } |
708 | static DEVICE_ATTR(l3_office_dim, 0664, adp8870_bl_l3_office_dim_show, | 707 | static DEVICE_ATTR(l3_office_dim, 0664, adp8870_bl_l3_office_dim_show, |
709 | adp8870_bl_l3_office_dim_store); | 708 | adp8870_bl_l3_office_dim_store); |
710 | 709 | ||
711 | static ssize_t adp8870_bl_l2_bright_dim_show(struct device *dev, | 710 | static ssize_t adp8870_bl_l2_bright_dim_show(struct device *dev, |
712 | struct device_attribute *attr, char *buf) | 711 | struct device_attribute *attr, char *buf) |
713 | { | 712 | { |
714 | return adp8870_show(dev, buf, ADP8870_BLDM2); | 713 | return adp8870_show(dev, buf, ADP8870_BLDM2); |
715 | } | 714 | } |
716 | 715 | ||
717 | static ssize_t adp8870_bl_l2_bright_dim_store(struct device *dev, | 716 | static ssize_t adp8870_bl_l2_bright_dim_store(struct device *dev, |
718 | struct device_attribute *attr, | 717 | struct device_attribute *attr, |
719 | const char *buf, size_t count) | 718 | const char *buf, size_t count) |
720 | { | 719 | { |
721 | return adp8870_store(dev, buf, count, ADP8870_BLDM2); | 720 | return adp8870_store(dev, buf, count, ADP8870_BLDM2); |
722 | } | 721 | } |
723 | static DEVICE_ATTR(l2_bright_dim, 0664, adp8870_bl_l2_bright_dim_show, | 722 | static DEVICE_ATTR(l2_bright_dim, 0664, adp8870_bl_l2_bright_dim_show, |
724 | adp8870_bl_l2_bright_dim_store); | 723 | adp8870_bl_l2_bright_dim_store); |
725 | 724 | ||
726 | static ssize_t adp8870_bl_l1_daylight_dim_show(struct device *dev, | 725 | static ssize_t adp8870_bl_l1_daylight_dim_show(struct device *dev, |
727 | struct device_attribute *attr, char *buf) | 726 | struct device_attribute *attr, char *buf) |
728 | { | 727 | { |
729 | return adp8870_show(dev, buf, ADP8870_BLDM1); | 728 | return adp8870_show(dev, buf, ADP8870_BLDM1); |
730 | } | 729 | } |
731 | 730 | ||
732 | static ssize_t adp8870_bl_l1_daylight_dim_store(struct device *dev, | 731 | static ssize_t adp8870_bl_l1_daylight_dim_store(struct device *dev, |
733 | struct device_attribute *attr, | 732 | struct device_attribute *attr, |
734 | const char *buf, size_t count) | 733 | const char *buf, size_t count) |
735 | { | 734 | { |
736 | return adp8870_store(dev, buf, count, ADP8870_BLDM1); | 735 | return adp8870_store(dev, buf, count, ADP8870_BLDM1); |
737 | } | 736 | } |
738 | static DEVICE_ATTR(l1_daylight_dim, 0664, adp8870_bl_l1_daylight_dim_show, | 737 | static DEVICE_ATTR(l1_daylight_dim, 0664, adp8870_bl_l1_daylight_dim_show, |
739 | adp8870_bl_l1_daylight_dim_store); | 738 | adp8870_bl_l1_daylight_dim_store); |
740 | 739 | ||
741 | #ifdef ADP8870_EXT_FEATURES | 740 | #ifdef ADP8870_EXT_FEATURES |
742 | static ssize_t adp8870_bl_ambient_light_level_show(struct device *dev, | 741 | static ssize_t adp8870_bl_ambient_light_level_show(struct device *dev, |
743 | struct device_attribute *attr, char *buf) | 742 | struct device_attribute *attr, char *buf) |
744 | { | 743 | { |
745 | struct adp8870_bl *data = dev_get_drvdata(dev); | 744 | struct adp8870_bl *data = dev_get_drvdata(dev); |
746 | int error; | 745 | int error; |
747 | uint8_t reg_val; | 746 | uint8_t reg_val; |
748 | uint16_t ret_val; | 747 | uint16_t ret_val; |
749 | 748 | ||
750 | mutex_lock(&data->lock); | 749 | mutex_lock(&data->lock); |
751 | error = adp8870_read(data->client, ADP8870_PH1LEVL, ®_val); | 750 | error = adp8870_read(data->client, ADP8870_PH1LEVL, ®_val); |
752 | if (error < 0) { | 751 | if (error < 0) { |
753 | mutex_unlock(&data->lock); | 752 | mutex_unlock(&data->lock); |
754 | return error; | 753 | return error; |
755 | } | 754 | } |
756 | ret_val = reg_val; | 755 | ret_val = reg_val; |
757 | error = adp8870_read(data->client, ADP8870_PH1LEVH, ®_val); | 756 | error = adp8870_read(data->client, ADP8870_PH1LEVH, ®_val); |
758 | mutex_unlock(&data->lock); | 757 | mutex_unlock(&data->lock); |
759 | 758 | ||
760 | if (error < 0) | 759 | if (error < 0) |
761 | return error; | 760 | return error; |
762 | 761 | ||
763 | /* Return 13-bit conversion value for the first light sensor */ | 762 | /* Return 13-bit conversion value for the first light sensor */ |
764 | ret_val += (reg_val & 0x1F) << 8; | 763 | ret_val += (reg_val & 0x1F) << 8; |
765 | 764 | ||
766 | return sprintf(buf, "%u\n", ret_val); | 765 | return sprintf(buf, "%u\n", ret_val); |
767 | } | 766 | } |
768 | static DEVICE_ATTR(ambient_light_level, 0444, | 767 | static DEVICE_ATTR(ambient_light_level, 0444, |
769 | adp8870_bl_ambient_light_level_show, NULL); | 768 | adp8870_bl_ambient_light_level_show, NULL); |
770 | 769 | ||
771 | static ssize_t adp8870_bl_ambient_light_zone_show(struct device *dev, | 770 | static ssize_t adp8870_bl_ambient_light_zone_show(struct device *dev, |
772 | struct device_attribute *attr, char *buf) | 771 | struct device_attribute *attr, char *buf) |
773 | { | 772 | { |
774 | struct adp8870_bl *data = dev_get_drvdata(dev); | 773 | struct adp8870_bl *data = dev_get_drvdata(dev); |
775 | int error; | 774 | int error; |
776 | uint8_t reg_val; | 775 | uint8_t reg_val; |
777 | 776 | ||
778 | mutex_lock(&data->lock); | 777 | mutex_lock(&data->lock); |
779 | error = adp8870_read(data->client, ADP8870_CFGR, ®_val); | 778 | error = adp8870_read(data->client, ADP8870_CFGR, ®_val); |
780 | mutex_unlock(&data->lock); | 779 | mutex_unlock(&data->lock); |
781 | 780 | ||
782 | if (error < 0) | 781 | if (error < 0) |
783 | return error; | 782 | return error; |
784 | 783 | ||
785 | return sprintf(buf, "%u\n", | 784 | return sprintf(buf, "%u\n", |
786 | ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); | 785 | ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); |
787 | } | 786 | } |
788 | 787 | ||
789 | static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev, | 788 | static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev, |
790 | struct device_attribute *attr, | 789 | struct device_attribute *attr, |
791 | const char *buf, size_t count) | 790 | const char *buf, size_t count) |
792 | { | 791 | { |
793 | struct adp8870_bl *data = dev_get_drvdata(dev); | 792 | struct adp8870_bl *data = dev_get_drvdata(dev); |
794 | unsigned long val; | 793 | unsigned long val; |
795 | uint8_t reg_val; | 794 | uint8_t reg_val; |
796 | int ret; | 795 | int ret; |
797 | 796 | ||
798 | ret = strict_strtoul(buf, 10, &val); | 797 | ret = strict_strtoul(buf, 10, &val); |
799 | if (ret) | 798 | if (ret) |
800 | return ret; | 799 | return ret; |
801 | 800 | ||
802 | if (val == 0) { | 801 | if (val == 0) { |
803 | /* Enable automatic ambient light sensing */ | 802 | /* Enable automatic ambient light sensing */ |
804 | adp8870_set_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); | 803 | adp8870_set_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); |
805 | } else if ((val > 0) && (val < 6)) { | 804 | } else if ((val > 0) && (val < 6)) { |
806 | /* Disable automatic ambient light sensing */ | 805 | /* Disable automatic ambient light sensing */ |
807 | adp8870_clr_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); | 806 | adp8870_clr_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); |
808 | 807 | ||
809 | /* Set user supplied ambient light zone */ | 808 | /* Set user supplied ambient light zone */ |
810 | mutex_lock(&data->lock); | 809 | mutex_lock(&data->lock); |
811 | adp8870_read(data->client, ADP8870_CFGR, ®_val); | 810 | adp8870_read(data->client, ADP8870_CFGR, ®_val); |
812 | reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); | 811 | reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); |
813 | reg_val |= (val - 1) << CFGR_BLV_SHIFT; | 812 | reg_val |= (val - 1) << CFGR_BLV_SHIFT; |
814 | adp8870_write(data->client, ADP8870_CFGR, reg_val); | 813 | adp8870_write(data->client, ADP8870_CFGR, reg_val); |
815 | mutex_unlock(&data->lock); | 814 | mutex_unlock(&data->lock); |
816 | } | 815 | } |
817 | 816 | ||
818 | return count; | 817 | return count; |
819 | } | 818 | } |
820 | static DEVICE_ATTR(ambient_light_zone, 0664, | 819 | static DEVICE_ATTR(ambient_light_zone, 0664, |
821 | adp8870_bl_ambient_light_zone_show, | 820 | adp8870_bl_ambient_light_zone_show, |
822 | adp8870_bl_ambient_light_zone_store); | 821 | adp8870_bl_ambient_light_zone_store); |
823 | #endif | 822 | #endif |
824 | 823 | ||
825 | static struct attribute *adp8870_bl_attributes[] = { | 824 | static struct attribute *adp8870_bl_attributes[] = { |
826 | &dev_attr_l5_dark_max.attr, | 825 | &dev_attr_l5_dark_max.attr, |
827 | &dev_attr_l5_dark_dim.attr, | 826 | &dev_attr_l5_dark_dim.attr, |
828 | &dev_attr_l4_indoor_max.attr, | 827 | &dev_attr_l4_indoor_max.attr, |
829 | &dev_attr_l4_indoor_dim.attr, | 828 | &dev_attr_l4_indoor_dim.attr, |
830 | &dev_attr_l3_office_max.attr, | 829 | &dev_attr_l3_office_max.attr, |
831 | &dev_attr_l3_office_dim.attr, | 830 | &dev_attr_l3_office_dim.attr, |
832 | &dev_attr_l2_bright_max.attr, | 831 | &dev_attr_l2_bright_max.attr, |
833 | &dev_attr_l2_bright_dim.attr, | 832 | &dev_attr_l2_bright_dim.attr, |
834 | &dev_attr_l1_daylight_max.attr, | 833 | &dev_attr_l1_daylight_max.attr, |
835 | &dev_attr_l1_daylight_dim.attr, | 834 | &dev_attr_l1_daylight_dim.attr, |
836 | #ifdef ADP8870_EXT_FEATURES | 835 | #ifdef ADP8870_EXT_FEATURES |
837 | &dev_attr_ambient_light_level.attr, | 836 | &dev_attr_ambient_light_level.attr, |
838 | &dev_attr_ambient_light_zone.attr, | 837 | &dev_attr_ambient_light_zone.attr, |
839 | #endif | 838 | #endif |
840 | NULL | 839 | NULL |
841 | }; | 840 | }; |
842 | 841 | ||
843 | static const struct attribute_group adp8870_bl_attr_group = { | 842 | static const struct attribute_group adp8870_bl_attr_group = { |
844 | .attrs = adp8870_bl_attributes, | 843 | .attrs = adp8870_bl_attributes, |
845 | }; | 844 | }; |
846 | 845 | ||
847 | static int __devinit adp8870_probe(struct i2c_client *client, | 846 | static int __devinit adp8870_probe(struct i2c_client *client, |
848 | const struct i2c_device_id *id) | 847 | const struct i2c_device_id *id) |
849 | { | 848 | { |
850 | struct backlight_properties props; | 849 | struct backlight_properties props; |
851 | struct backlight_device *bl; | 850 | struct backlight_device *bl; |
852 | struct adp8870_bl *data; | 851 | struct adp8870_bl *data; |
853 | struct adp8870_backlight_platform_data *pdata = | 852 | struct adp8870_backlight_platform_data *pdata = |
854 | client->dev.platform_data; | 853 | client->dev.platform_data; |
855 | uint8_t reg_val; | 854 | uint8_t reg_val; |
856 | int ret; | 855 | int ret; |
857 | 856 | ||
858 | if (!i2c_check_functionality(client->adapter, | 857 | if (!i2c_check_functionality(client->adapter, |
859 | I2C_FUNC_SMBUS_BYTE_DATA)) { | 858 | I2C_FUNC_SMBUS_BYTE_DATA)) { |
860 | dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); | 859 | dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); |
861 | return -EIO; | 860 | return -EIO; |
862 | } | 861 | } |
863 | 862 | ||
864 | if (!pdata) { | 863 | if (!pdata) { |
865 | dev_err(&client->dev, "no platform data?\n"); | 864 | dev_err(&client->dev, "no platform data?\n"); |
866 | return -EINVAL; | 865 | return -EINVAL; |
867 | } | 866 | } |
868 | 867 | ||
869 | ret = adp8870_read(client, ADP8870_MFDVID, ®_val); | 868 | ret = adp8870_read(client, ADP8870_MFDVID, ®_val); |
870 | if (ret < 0) | 869 | if (ret < 0) |
871 | return -EIO; | 870 | return -EIO; |
872 | 871 | ||
873 | if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) { | 872 | if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) { |
874 | dev_err(&client->dev, "failed to probe\n"); | 873 | dev_err(&client->dev, "failed to probe\n"); |
875 | return -ENODEV; | 874 | return -ENODEV; |
876 | } | 875 | } |
877 | 876 | ||
878 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 877 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
879 | if (data == NULL) | 878 | if (data == NULL) |
880 | return -ENOMEM; | 879 | return -ENOMEM; |
881 | 880 | ||
882 | data->revid = ADP8870_DEVID(reg_val); | 881 | data->revid = ADP8870_DEVID(reg_val); |
883 | data->client = client; | 882 | data->client = client; |
884 | data->pdata = pdata; | 883 | data->pdata = pdata; |
885 | data->id = id->driver_data; | 884 | data->id = id->driver_data; |
886 | data->current_brightness = 0; | 885 | data->current_brightness = 0; |
887 | i2c_set_clientdata(client, data); | 886 | i2c_set_clientdata(client, data); |
888 | 887 | ||
889 | mutex_init(&data->lock); | 888 | mutex_init(&data->lock); |
890 | 889 | ||
891 | memset(&props, 0, sizeof(props)); | 890 | memset(&props, 0, sizeof(props)); |
892 | props.type = BACKLIGHT_RAW; | 891 | props.type = BACKLIGHT_RAW; |
893 | props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS; | 892 | props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS; |
894 | bl = backlight_device_register(dev_driver_string(&client->dev), | 893 | bl = backlight_device_register(dev_driver_string(&client->dev), |
895 | &client->dev, data, &adp8870_bl_ops, &props); | 894 | &client->dev, data, &adp8870_bl_ops, &props); |
896 | if (IS_ERR(bl)) { | 895 | if (IS_ERR(bl)) { |
897 | dev_err(&client->dev, "failed to register backlight\n"); | 896 | dev_err(&client->dev, "failed to register backlight\n"); |
898 | ret = PTR_ERR(bl); | 897 | ret = PTR_ERR(bl); |
899 | goto out2; | 898 | goto out2; |
900 | } | 899 | } |
901 | 900 | ||
902 | data->bl = bl; | 901 | data->bl = bl; |
903 | 902 | ||
904 | if (pdata->en_ambl_sens) | 903 | if (pdata->en_ambl_sens) |
905 | ret = sysfs_create_group(&bl->dev.kobj, | 904 | ret = sysfs_create_group(&bl->dev.kobj, |
906 | &adp8870_bl_attr_group); | 905 | &adp8870_bl_attr_group); |
907 | 906 | ||
908 | if (ret) { | 907 | if (ret) { |
909 | dev_err(&client->dev, "failed to register sysfs\n"); | 908 | dev_err(&client->dev, "failed to register sysfs\n"); |
910 | goto out1; | 909 | goto out1; |
911 | } | 910 | } |
912 | 911 | ||
913 | ret = adp8870_bl_setup(bl); | 912 | ret = adp8870_bl_setup(bl); |
914 | if (ret) { | 913 | if (ret) { |
915 | ret = -EIO; | 914 | ret = -EIO; |
916 | goto out; | 915 | goto out; |
917 | } | 916 | } |
918 | 917 | ||
919 | backlight_update_status(bl); | 918 | backlight_update_status(bl); |
920 | 919 | ||
921 | dev_info(&client->dev, "Rev.%d Backlight\n", data->revid); | 920 | dev_info(&client->dev, "Rev.%d Backlight\n", data->revid); |
922 | 921 | ||
923 | if (pdata->num_leds) | 922 | if (pdata->num_leds) |
924 | adp8870_led_probe(client); | 923 | adp8870_led_probe(client); |
925 | 924 | ||
926 | return 0; | 925 | return 0; |
927 | 926 | ||
928 | out: | 927 | out: |
929 | if (data->pdata->en_ambl_sens) | 928 | if (data->pdata->en_ambl_sens) |
930 | sysfs_remove_group(&data->bl->dev.kobj, | 929 | sysfs_remove_group(&data->bl->dev.kobj, |
931 | &adp8870_bl_attr_group); | 930 | &adp8870_bl_attr_group); |
932 | out1: | 931 | out1: |
933 | backlight_device_unregister(bl); | 932 | backlight_device_unregister(bl); |
934 | out2: | 933 | out2: |
935 | i2c_set_clientdata(client, NULL); | 934 | i2c_set_clientdata(client, NULL); |
936 | kfree(data); | 935 | kfree(data); |
937 | 936 | ||
938 | return ret; | 937 | return ret; |
939 | } | 938 | } |
940 | 939 | ||
941 | static int __devexit adp8870_remove(struct i2c_client *client) | 940 | static int __devexit adp8870_remove(struct i2c_client *client) |
942 | { | 941 | { |
943 | struct adp8870_bl *data = i2c_get_clientdata(client); | 942 | struct adp8870_bl *data = i2c_get_clientdata(client); |
944 | 943 | ||
945 | adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); | 944 | adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); |
946 | 945 | ||
947 | if (data->led) | 946 | if (data->led) |
948 | adp8870_led_remove(client); | 947 | adp8870_led_remove(client); |
949 | 948 | ||
950 | if (data->pdata->en_ambl_sens) | 949 | if (data->pdata->en_ambl_sens) |
951 | sysfs_remove_group(&data->bl->dev.kobj, | 950 | sysfs_remove_group(&data->bl->dev.kobj, |
952 | &adp8870_bl_attr_group); | 951 | &adp8870_bl_attr_group); |
953 | 952 | ||
954 | backlight_device_unregister(data->bl); | 953 | backlight_device_unregister(data->bl); |
955 | i2c_set_clientdata(client, NULL); | 954 | i2c_set_clientdata(client, NULL); |
956 | kfree(data); | 955 | kfree(data); |
957 | 956 | ||
958 | return 0; | 957 | return 0; |
959 | } | 958 | } |
960 | 959 | ||
961 | #ifdef CONFIG_PM | 960 | #ifdef CONFIG_PM |
962 | static int adp8870_i2c_suspend(struct i2c_client *client, pm_message_t message) | 961 | static int adp8870_i2c_suspend(struct i2c_client *client, pm_message_t message) |
963 | { | 962 | { |
964 | adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); | 963 | adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); |
965 | 964 | ||
966 | return 0; | 965 | return 0; |
967 | } | 966 | } |
968 | 967 | ||
969 | static int adp8870_i2c_resume(struct i2c_client *client) | 968 | static int adp8870_i2c_resume(struct i2c_client *client) |
970 | { | 969 | { |
971 | adp8870_set_bits(client, ADP8870_MDCR, NSTBY); | 970 | adp8870_set_bits(client, ADP8870_MDCR, NSTBY); |
972 | 971 | ||
973 | return 0; | 972 | return 0; |
974 | } | 973 | } |
975 | #else | 974 | #else |
976 | #define adp8870_i2c_suspend NULL | 975 | #define adp8870_i2c_suspend NULL |
977 | #define adp8870_i2c_resume NULL | 976 | #define adp8870_i2c_resume NULL |
978 | #endif | 977 | #endif |
979 | 978 | ||
980 | static const struct i2c_device_id adp8870_id[] = { | 979 | static const struct i2c_device_id adp8870_id[] = { |
981 | { "adp8870", 0 }, | 980 | { "adp8870", 0 }, |
982 | { } | 981 | { } |
983 | }; | 982 | }; |
984 | MODULE_DEVICE_TABLE(i2c, adp8870_id); | 983 | MODULE_DEVICE_TABLE(i2c, adp8870_id); |
985 | 984 | ||
986 | static struct i2c_driver adp8870_driver = { | 985 | static struct i2c_driver adp8870_driver = { |
987 | .driver = { | 986 | .driver = { |
988 | .name = KBUILD_MODNAME, | 987 | .name = KBUILD_MODNAME, |
989 | }, | 988 | }, |
990 | .probe = adp8870_probe, | 989 | .probe = adp8870_probe, |
991 | .remove = __devexit_p(adp8870_remove), | 990 | .remove = __devexit_p(adp8870_remove), |
992 | .suspend = adp8870_i2c_suspend, | 991 | .suspend = adp8870_i2c_suspend, |
993 | .resume = adp8870_i2c_resume, | 992 | .resume = adp8870_i2c_resume, |
994 | .id_table = adp8870_id, | 993 | .id_table = adp8870_id, |
995 | }; | 994 | }; |
996 | 995 | ||
997 | static int __init adp8870_init(void) | 996 | static int __init adp8870_init(void) |
998 | { | 997 | { |
999 | return i2c_add_driver(&adp8870_driver); | 998 | return i2c_add_driver(&adp8870_driver); |
1000 | } | 999 | } |
1001 | module_init(adp8870_init); | 1000 | module_init(adp8870_init); |
1002 | 1001 | ||
1003 | static void __exit adp8870_exit(void) | 1002 | static void __exit adp8870_exit(void) |
1004 | { | 1003 | { |
1005 | i2c_del_driver(&adp8870_driver); | 1004 | i2c_del_driver(&adp8870_driver); |
1006 | } | 1005 | } |
1007 | module_exit(adp8870_exit); | 1006 | module_exit(adp8870_exit); |
1008 | 1007 | ||
1009 | MODULE_LICENSE("GPL v2"); | 1008 | MODULE_LICENSE("GPL v2"); |
1010 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | 1009 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); |
1011 | MODULE_DESCRIPTION("ADP8870 Backlight driver"); | 1010 | MODULE_DESCRIPTION("ADP8870 Backlight driver"); |
1012 | MODULE_ALIAS("platform:adp8870-backlight"); | 1011 | MODULE_ALIAS("platform:adp8870-backlight"); |
1013 | 1012 |
drivers/video/pxa3xx-gcu.c
1 | /* | 1 | /* |
2 | * pxa3xx-gcu.c - Linux kernel module for PXA3xx graphics controllers | 2 | * pxa3xx-gcu.c - Linux kernel module for PXA3xx graphics controllers |
3 | * | 3 | * |
4 | * This driver needs a DirectFB counterpart in user space, communication | 4 | * This driver needs a DirectFB counterpart in user space, communication |
5 | * is handled via mmap()ed memory areas and an ioctl. | 5 | * is handled via mmap()ed memory areas and an ioctl. |
6 | * | 6 | * |
7 | * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> | 7 | * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> |
8 | * Copyright (c) 2009 Janine Kropp <nin@directfb.org> | 8 | * Copyright (c) 2009 Janine Kropp <nin@directfb.org> |
9 | * Copyright (c) 2009 Denis Oliver Kropp <dok@directfb.org> | 9 | * Copyright (c) 2009 Denis Oliver Kropp <dok@directfb.org> |
10 | * | 10 | * |
11 | * This program is free software; you can redistribute it and/or modify | 11 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License as published by | 12 | * it under the terms of the GNU General Public License as published by |
13 | * the Free Software Foundation; either version 2 of the License, or | 13 | * the Free Software Foundation; either version 2 of the License, or |
14 | * (at your option) any later version. | 14 | * (at your option) any later version. |
15 | * | 15 | * |
16 | * This program is distributed in the hope that it will be useful, | 16 | * This program is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | * GNU General Public License for more details. | 19 | * GNU General Public License for more details. |
20 | * | 20 | * |
21 | * You should have received a copy of the GNU General Public License | 21 | * You should have received a copy of the GNU General Public License |
22 | * along with this program; if not, write to the Free Software | 22 | * along with this program; if not, write to the Free Software |
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
24 | */ | 24 | */ |
25 | 25 | ||
26 | /* | 26 | /* |
27 | * WARNING: This controller is attached to System Bus 2 of the PXA which | 27 | * WARNING: This controller is attached to System Bus 2 of the PXA which |
28 | * needs its arbiter to be enabled explicitly (CKENB & 1<<9). | 28 | * needs its arbiter to be enabled explicitly (CKENB & 1<<9). |
29 | * There is currently no way to do this from Linux, so you need to teach | 29 | * There is currently no way to do this from Linux, so you need to teach |
30 | * your bootloader for now. | 30 | * your bootloader for now. |
31 | */ | 31 | */ |
32 | 32 | ||
33 | #include <linux/module.h> | 33 | #include <linux/module.h> |
34 | #include <linux/version.h> | ||
35 | |||
36 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
37 | #include <linux/dma-mapping.h> | 35 | #include <linux/dma-mapping.h> |
38 | #include <linux/miscdevice.h> | 36 | #include <linux/miscdevice.h> |
39 | #include <linux/interrupt.h> | 37 | #include <linux/interrupt.h> |
40 | #include <linux/spinlock.h> | 38 | #include <linux/spinlock.h> |
41 | #include <linux/uaccess.h> | 39 | #include <linux/uaccess.h> |
42 | #include <linux/ioctl.h> | 40 | #include <linux/ioctl.h> |
43 | #include <linux/delay.h> | 41 | #include <linux/delay.h> |
44 | #include <linux/sched.h> | 42 | #include <linux/sched.h> |
45 | #include <linux/slab.h> | 43 | #include <linux/slab.h> |
46 | #include <linux/clk.h> | 44 | #include <linux/clk.h> |
47 | #include <linux/fs.h> | 45 | #include <linux/fs.h> |
48 | #include <linux/io.h> | 46 | #include <linux/io.h> |
49 | 47 | ||
50 | #include "pxa3xx-gcu.h" | 48 | #include "pxa3xx-gcu.h" |
51 | 49 | ||
52 | #define DRV_NAME "pxa3xx-gcu" | 50 | #define DRV_NAME "pxa3xx-gcu" |
53 | #define MISCDEV_MINOR 197 | 51 | #define MISCDEV_MINOR 197 |
54 | 52 | ||
55 | #define REG_GCCR 0x00 | 53 | #define REG_GCCR 0x00 |
56 | #define GCCR_SYNC_CLR (1 << 9) | 54 | #define GCCR_SYNC_CLR (1 << 9) |
57 | #define GCCR_BP_RST (1 << 8) | 55 | #define GCCR_BP_RST (1 << 8) |
58 | #define GCCR_ABORT (1 << 6) | 56 | #define GCCR_ABORT (1 << 6) |
59 | #define GCCR_STOP (1 << 4) | 57 | #define GCCR_STOP (1 << 4) |
60 | 58 | ||
61 | #define REG_GCISCR 0x04 | 59 | #define REG_GCISCR 0x04 |
62 | #define REG_GCIECR 0x08 | 60 | #define REG_GCIECR 0x08 |
63 | #define REG_GCRBBR 0x20 | 61 | #define REG_GCRBBR 0x20 |
64 | #define REG_GCRBLR 0x24 | 62 | #define REG_GCRBLR 0x24 |
65 | #define REG_GCRBHR 0x28 | 63 | #define REG_GCRBHR 0x28 |
66 | #define REG_GCRBTR 0x2C | 64 | #define REG_GCRBTR 0x2C |
67 | #define REG_GCRBEXHR 0x30 | 65 | #define REG_GCRBEXHR 0x30 |
68 | 66 | ||
69 | #define IE_EOB (1 << 0) | 67 | #define IE_EOB (1 << 0) |
70 | #define IE_EEOB (1 << 5) | 68 | #define IE_EEOB (1 << 5) |
71 | #define IE_ALL 0xff | 69 | #define IE_ALL 0xff |
72 | 70 | ||
73 | #define SHARED_SIZE PAGE_ALIGN(sizeof(struct pxa3xx_gcu_shared)) | 71 | #define SHARED_SIZE PAGE_ALIGN(sizeof(struct pxa3xx_gcu_shared)) |
74 | 72 | ||
75 | /* #define PXA3XX_GCU_DEBUG */ | 73 | /* #define PXA3XX_GCU_DEBUG */ |
76 | /* #define PXA3XX_GCU_DEBUG_TIMER */ | 74 | /* #define PXA3XX_GCU_DEBUG_TIMER */ |
77 | 75 | ||
78 | #ifdef PXA3XX_GCU_DEBUG | 76 | #ifdef PXA3XX_GCU_DEBUG |
79 | #define QDUMP(msg) \ | 77 | #define QDUMP(msg) \ |
80 | do { \ | 78 | do { \ |
81 | QPRINT(priv, KERN_DEBUG, msg); \ | 79 | QPRINT(priv, KERN_DEBUG, msg); \ |
82 | } while (0) | 80 | } while (0) |
83 | #else | 81 | #else |
84 | #define QDUMP(msg) do {} while (0) | 82 | #define QDUMP(msg) do {} while (0) |
85 | #endif | 83 | #endif |
86 | 84 | ||
87 | #define QERROR(msg) \ | 85 | #define QERROR(msg) \ |
88 | do { \ | 86 | do { \ |
89 | QPRINT(priv, KERN_ERR, msg); \ | 87 | QPRINT(priv, KERN_ERR, msg); \ |
90 | } while (0) | 88 | } while (0) |
91 | 89 | ||
92 | struct pxa3xx_gcu_batch { | 90 | struct pxa3xx_gcu_batch { |
93 | struct pxa3xx_gcu_batch *next; | 91 | struct pxa3xx_gcu_batch *next; |
94 | u32 *ptr; | 92 | u32 *ptr; |
95 | dma_addr_t phys; | 93 | dma_addr_t phys; |
96 | unsigned long length; | 94 | unsigned long length; |
97 | }; | 95 | }; |
98 | 96 | ||
99 | struct pxa3xx_gcu_priv { | 97 | struct pxa3xx_gcu_priv { |
100 | void __iomem *mmio_base; | 98 | void __iomem *mmio_base; |
101 | struct clk *clk; | 99 | struct clk *clk; |
102 | struct pxa3xx_gcu_shared *shared; | 100 | struct pxa3xx_gcu_shared *shared; |
103 | dma_addr_t shared_phys; | 101 | dma_addr_t shared_phys; |
104 | struct resource *resource_mem; | 102 | struct resource *resource_mem; |
105 | struct miscdevice misc_dev; | 103 | struct miscdevice misc_dev; |
106 | struct file_operations misc_fops; | 104 | struct file_operations misc_fops; |
107 | wait_queue_head_t wait_idle; | 105 | wait_queue_head_t wait_idle; |
108 | wait_queue_head_t wait_free; | 106 | wait_queue_head_t wait_free; |
109 | spinlock_t spinlock; | 107 | spinlock_t spinlock; |
110 | struct timeval base_time; | 108 | struct timeval base_time; |
111 | 109 | ||
112 | struct pxa3xx_gcu_batch *free; | 110 | struct pxa3xx_gcu_batch *free; |
113 | 111 | ||
114 | struct pxa3xx_gcu_batch *ready; | 112 | struct pxa3xx_gcu_batch *ready; |
115 | struct pxa3xx_gcu_batch *ready_last; | 113 | struct pxa3xx_gcu_batch *ready_last; |
116 | struct pxa3xx_gcu_batch *running; | 114 | struct pxa3xx_gcu_batch *running; |
117 | }; | 115 | }; |
118 | 116 | ||
119 | static inline unsigned long | 117 | static inline unsigned long |
120 | gc_readl(struct pxa3xx_gcu_priv *priv, unsigned int off) | 118 | gc_readl(struct pxa3xx_gcu_priv *priv, unsigned int off) |
121 | { | 119 | { |
122 | return __raw_readl(priv->mmio_base + off); | 120 | return __raw_readl(priv->mmio_base + off); |
123 | } | 121 | } |
124 | 122 | ||
125 | static inline void | 123 | static inline void |
126 | gc_writel(struct pxa3xx_gcu_priv *priv, unsigned int off, unsigned long val) | 124 | gc_writel(struct pxa3xx_gcu_priv *priv, unsigned int off, unsigned long val) |
127 | { | 125 | { |
128 | __raw_writel(val, priv->mmio_base + off); | 126 | __raw_writel(val, priv->mmio_base + off); |
129 | } | 127 | } |
130 | 128 | ||
131 | #define QPRINT(priv, level, msg) \ | 129 | #define QPRINT(priv, level, msg) \ |
132 | do { \ | 130 | do { \ |
133 | struct timeval tv; \ | 131 | struct timeval tv; \ |
134 | struct pxa3xx_gcu_shared *shared = priv->shared; \ | 132 | struct pxa3xx_gcu_shared *shared = priv->shared; \ |
135 | u32 base = gc_readl(priv, REG_GCRBBR); \ | 133 | u32 base = gc_readl(priv, REG_GCRBBR); \ |
136 | \ | 134 | \ |
137 | do_gettimeofday(&tv); \ | 135 | do_gettimeofday(&tv); \ |
138 | \ | 136 | \ |
139 | printk(level "%ld.%03ld.%03ld - %-17s: %-21s (%s, " \ | 137 | printk(level "%ld.%03ld.%03ld - %-17s: %-21s (%s, " \ |
140 | "STATUS " \ | 138 | "STATUS " \ |
141 | "0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, " \ | 139 | "0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, " \ |
142 | "T %5ld)\n", \ | 140 | "T %5ld)\n", \ |
143 | tv.tv_sec - priv->base_time.tv_sec, \ | 141 | tv.tv_sec - priv->base_time.tv_sec, \ |
144 | tv.tv_usec / 1000, tv.tv_usec % 1000, \ | 142 | tv.tv_usec / 1000, tv.tv_usec % 1000, \ |
145 | __func__, msg, \ | 143 | __func__, msg, \ |
146 | shared->hw_running ? "running" : " idle", \ | 144 | shared->hw_running ? "running" : " idle", \ |
147 | gc_readl(priv, REG_GCISCR), \ | 145 | gc_readl(priv, REG_GCISCR), \ |
148 | gc_readl(priv, REG_GCRBBR), \ | 146 | gc_readl(priv, REG_GCRBBR), \ |
149 | gc_readl(priv, REG_GCRBLR), \ | 147 | gc_readl(priv, REG_GCRBLR), \ |
150 | (gc_readl(priv, REG_GCRBEXHR) - base) / 4, \ | 148 | (gc_readl(priv, REG_GCRBEXHR) - base) / 4, \ |
151 | (gc_readl(priv, REG_GCRBHR) - base) / 4, \ | 149 | (gc_readl(priv, REG_GCRBHR) - base) / 4, \ |
152 | (gc_readl(priv, REG_GCRBTR) - base) / 4); \ | 150 | (gc_readl(priv, REG_GCRBTR) - base) / 4); \ |
153 | } while (0) | 151 | } while (0) |
154 | 152 | ||
155 | static void | 153 | static void |
156 | pxa3xx_gcu_reset(struct pxa3xx_gcu_priv *priv) | 154 | pxa3xx_gcu_reset(struct pxa3xx_gcu_priv *priv) |
157 | { | 155 | { |
158 | QDUMP("RESET"); | 156 | QDUMP("RESET"); |
159 | 157 | ||
160 | /* disable interrupts */ | 158 | /* disable interrupts */ |
161 | gc_writel(priv, REG_GCIECR, 0); | 159 | gc_writel(priv, REG_GCIECR, 0); |
162 | 160 | ||
163 | /* reset hardware */ | 161 | /* reset hardware */ |
164 | gc_writel(priv, REG_GCCR, GCCR_ABORT); | 162 | gc_writel(priv, REG_GCCR, GCCR_ABORT); |
165 | gc_writel(priv, REG_GCCR, 0); | 163 | gc_writel(priv, REG_GCCR, 0); |
166 | 164 | ||
167 | memset(priv->shared, 0, SHARED_SIZE); | 165 | memset(priv->shared, 0, SHARED_SIZE); |
168 | priv->shared->buffer_phys = priv->shared_phys; | 166 | priv->shared->buffer_phys = priv->shared_phys; |
169 | priv->shared->magic = PXA3XX_GCU_SHARED_MAGIC; | 167 | priv->shared->magic = PXA3XX_GCU_SHARED_MAGIC; |
170 | 168 | ||
171 | do_gettimeofday(&priv->base_time); | 169 | do_gettimeofday(&priv->base_time); |
172 | 170 | ||
173 | /* set up the ring buffer pointers */ | 171 | /* set up the ring buffer pointers */ |
174 | gc_writel(priv, REG_GCRBLR, 0); | 172 | gc_writel(priv, REG_GCRBLR, 0); |
175 | gc_writel(priv, REG_GCRBBR, priv->shared_phys); | 173 | gc_writel(priv, REG_GCRBBR, priv->shared_phys); |
176 | gc_writel(priv, REG_GCRBTR, priv->shared_phys); | 174 | gc_writel(priv, REG_GCRBTR, priv->shared_phys); |
177 | 175 | ||
178 | /* enable all IRQs except EOB */ | 176 | /* enable all IRQs except EOB */ |
179 | gc_writel(priv, REG_GCIECR, IE_ALL & ~IE_EOB); | 177 | gc_writel(priv, REG_GCIECR, IE_ALL & ~IE_EOB); |
180 | } | 178 | } |
181 | 179 | ||
182 | static void | 180 | static void |
183 | dump_whole_state(struct pxa3xx_gcu_priv *priv) | 181 | dump_whole_state(struct pxa3xx_gcu_priv *priv) |
184 | { | 182 | { |
185 | struct pxa3xx_gcu_shared *sh = priv->shared; | 183 | struct pxa3xx_gcu_shared *sh = priv->shared; |
186 | u32 base = gc_readl(priv, REG_GCRBBR); | 184 | u32 base = gc_readl(priv, REG_GCRBBR); |
187 | 185 | ||
188 | QDUMP("DUMP"); | 186 | QDUMP("DUMP"); |
189 | 187 | ||
190 | printk(KERN_DEBUG "== PXA3XX-GCU DUMP ==\n" | 188 | printk(KERN_DEBUG "== PXA3XX-GCU DUMP ==\n" |
191 | "%s, STATUS 0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, T %5ld\n", | 189 | "%s, STATUS 0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, T %5ld\n", |
192 | sh->hw_running ? "running" : "idle ", | 190 | sh->hw_running ? "running" : "idle ", |
193 | gc_readl(priv, REG_GCISCR), | 191 | gc_readl(priv, REG_GCISCR), |
194 | gc_readl(priv, REG_GCRBBR), | 192 | gc_readl(priv, REG_GCRBBR), |
195 | gc_readl(priv, REG_GCRBLR), | 193 | gc_readl(priv, REG_GCRBLR), |
196 | (gc_readl(priv, REG_GCRBEXHR) - base) / 4, | 194 | (gc_readl(priv, REG_GCRBEXHR) - base) / 4, |
197 | (gc_readl(priv, REG_GCRBHR) - base) / 4, | 195 | (gc_readl(priv, REG_GCRBHR) - base) / 4, |
198 | (gc_readl(priv, REG_GCRBTR) - base) / 4); | 196 | (gc_readl(priv, REG_GCRBTR) - base) / 4); |
199 | } | 197 | } |
200 | 198 | ||
201 | static void | 199 | static void |
202 | flush_running(struct pxa3xx_gcu_priv *priv) | 200 | flush_running(struct pxa3xx_gcu_priv *priv) |
203 | { | 201 | { |
204 | struct pxa3xx_gcu_batch *running = priv->running; | 202 | struct pxa3xx_gcu_batch *running = priv->running; |
205 | struct pxa3xx_gcu_batch *next; | 203 | struct pxa3xx_gcu_batch *next; |
206 | 204 | ||
207 | while (running) { | 205 | while (running) { |
208 | next = running->next; | 206 | next = running->next; |
209 | running->next = priv->free; | 207 | running->next = priv->free; |
210 | priv->free = running; | 208 | priv->free = running; |
211 | running = next; | 209 | running = next; |
212 | } | 210 | } |
213 | 211 | ||
214 | priv->running = NULL; | 212 | priv->running = NULL; |
215 | } | 213 | } |
216 | 214 | ||
217 | static void | 215 | static void |
218 | run_ready(struct pxa3xx_gcu_priv *priv) | 216 | run_ready(struct pxa3xx_gcu_priv *priv) |
219 | { | 217 | { |
220 | unsigned int num = 0; | 218 | unsigned int num = 0; |
221 | struct pxa3xx_gcu_shared *shared = priv->shared; | 219 | struct pxa3xx_gcu_shared *shared = priv->shared; |
222 | struct pxa3xx_gcu_batch *ready = priv->ready; | 220 | struct pxa3xx_gcu_batch *ready = priv->ready; |
223 | 221 | ||
224 | QDUMP("Start"); | 222 | QDUMP("Start"); |
225 | 223 | ||
226 | BUG_ON(!ready); | 224 | BUG_ON(!ready); |
227 | 225 | ||
228 | shared->buffer[num++] = 0x05000000; | 226 | shared->buffer[num++] = 0x05000000; |
229 | 227 | ||
230 | while (ready) { | 228 | while (ready) { |
231 | shared->buffer[num++] = 0x00000001; | 229 | shared->buffer[num++] = 0x00000001; |
232 | shared->buffer[num++] = ready->phys; | 230 | shared->buffer[num++] = ready->phys; |
233 | ready = ready->next; | 231 | ready = ready->next; |
234 | } | 232 | } |
235 | 233 | ||
236 | shared->buffer[num++] = 0x05000000; | 234 | shared->buffer[num++] = 0x05000000; |
237 | priv->running = priv->ready; | 235 | priv->running = priv->ready; |
238 | priv->ready = priv->ready_last = NULL; | 236 | priv->ready = priv->ready_last = NULL; |
239 | gc_writel(priv, REG_GCRBLR, 0); | 237 | gc_writel(priv, REG_GCRBLR, 0); |
240 | shared->hw_running = 1; | 238 | shared->hw_running = 1; |
241 | 239 | ||
242 | /* ring base address */ | 240 | /* ring base address */ |
243 | gc_writel(priv, REG_GCRBBR, shared->buffer_phys); | 241 | gc_writel(priv, REG_GCRBBR, shared->buffer_phys); |
244 | 242 | ||
245 | /* ring tail address */ | 243 | /* ring tail address */ |
246 | gc_writel(priv, REG_GCRBTR, shared->buffer_phys + num * 4); | 244 | gc_writel(priv, REG_GCRBTR, shared->buffer_phys + num * 4); |
247 | 245 | ||
248 | /* ring length */ | 246 | /* ring length */ |
249 | gc_writel(priv, REG_GCRBLR, ((num + 63) & ~63) * 4); | 247 | gc_writel(priv, REG_GCRBLR, ((num + 63) & ~63) * 4); |
250 | } | 248 | } |
251 | 249 | ||
252 | static irqreturn_t | 250 | static irqreturn_t |
253 | pxa3xx_gcu_handle_irq(int irq, void *ctx) | 251 | pxa3xx_gcu_handle_irq(int irq, void *ctx) |
254 | { | 252 | { |
255 | struct pxa3xx_gcu_priv *priv = ctx; | 253 | struct pxa3xx_gcu_priv *priv = ctx; |
256 | struct pxa3xx_gcu_shared *shared = priv->shared; | 254 | struct pxa3xx_gcu_shared *shared = priv->shared; |
257 | u32 status = gc_readl(priv, REG_GCISCR) & IE_ALL; | 255 | u32 status = gc_readl(priv, REG_GCISCR) & IE_ALL; |
258 | 256 | ||
259 | QDUMP("-Interrupt"); | 257 | QDUMP("-Interrupt"); |
260 | 258 | ||
261 | if (!status) | 259 | if (!status) |
262 | return IRQ_NONE; | 260 | return IRQ_NONE; |
263 | 261 | ||
264 | spin_lock(&priv->spinlock); | 262 | spin_lock(&priv->spinlock); |
265 | shared->num_interrupts++; | 263 | shared->num_interrupts++; |
266 | 264 | ||
267 | if (status & IE_EEOB) { | 265 | if (status & IE_EEOB) { |
268 | QDUMP(" [EEOB]"); | 266 | QDUMP(" [EEOB]"); |
269 | 267 | ||
270 | flush_running(priv); | 268 | flush_running(priv); |
271 | wake_up_all(&priv->wait_free); | 269 | wake_up_all(&priv->wait_free); |
272 | 270 | ||
273 | if (priv->ready) { | 271 | if (priv->ready) { |
274 | run_ready(priv); | 272 | run_ready(priv); |
275 | } else { | 273 | } else { |
276 | /* There is no more data prepared by the userspace. | 274 | /* There is no more data prepared by the userspace. |
277 | * Set hw_running = 0 and wait for the next userspace | 275 | * Set hw_running = 0 and wait for the next userspace |
278 | * kick-off */ | 276 | * kick-off */ |
279 | shared->num_idle++; | 277 | shared->num_idle++; |
280 | shared->hw_running = 0; | 278 | shared->hw_running = 0; |
281 | 279 | ||
282 | QDUMP(" '-> Idle."); | 280 | QDUMP(" '-> Idle."); |
283 | 281 | ||
284 | /* set ring buffer length to zero */ | 282 | /* set ring buffer length to zero */ |
285 | gc_writel(priv, REG_GCRBLR, 0); | 283 | gc_writel(priv, REG_GCRBLR, 0); |
286 | 284 | ||
287 | wake_up_all(&priv->wait_idle); | 285 | wake_up_all(&priv->wait_idle); |
288 | } | 286 | } |
289 | 287 | ||
290 | shared->num_done++; | 288 | shared->num_done++; |
291 | } else { | 289 | } else { |
292 | QERROR(" [???]"); | 290 | QERROR(" [???]"); |
293 | dump_whole_state(priv); | 291 | dump_whole_state(priv); |
294 | } | 292 | } |
295 | 293 | ||
296 | /* Clear the interrupt */ | 294 | /* Clear the interrupt */ |
297 | gc_writel(priv, REG_GCISCR, status); | 295 | gc_writel(priv, REG_GCISCR, status); |
298 | spin_unlock(&priv->spinlock); | 296 | spin_unlock(&priv->spinlock); |
299 | 297 | ||
300 | return IRQ_HANDLED; | 298 | return IRQ_HANDLED; |
301 | } | 299 | } |
302 | 300 | ||
303 | static int | 301 | static int |
304 | pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv) | 302 | pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv) |
305 | { | 303 | { |
306 | int ret = 0; | 304 | int ret = 0; |
307 | 305 | ||
308 | QDUMP("Waiting for idle..."); | 306 | QDUMP("Waiting for idle..."); |
309 | 307 | ||
310 | /* Does not need to be atomic. There's a lock in user space, | 308 | /* Does not need to be atomic. There's a lock in user space, |
311 | * but anyhow, this is just for statistics. */ | 309 | * but anyhow, this is just for statistics. */ |
312 | priv->shared->num_wait_idle++; | 310 | priv->shared->num_wait_idle++; |
313 | 311 | ||
314 | while (priv->shared->hw_running) { | 312 | while (priv->shared->hw_running) { |
315 | int num = priv->shared->num_interrupts; | 313 | int num = priv->shared->num_interrupts; |
316 | u32 rbexhr = gc_readl(priv, REG_GCRBEXHR); | 314 | u32 rbexhr = gc_readl(priv, REG_GCRBEXHR); |
317 | 315 | ||
318 | ret = wait_event_interruptible_timeout(priv->wait_idle, | 316 | ret = wait_event_interruptible_timeout(priv->wait_idle, |
319 | !priv->shared->hw_running, HZ*4); | 317 | !priv->shared->hw_running, HZ*4); |
320 | 318 | ||
321 | if (ret < 0) | 319 | if (ret < 0) |
322 | break; | 320 | break; |
323 | 321 | ||
324 | if (ret > 0) | 322 | if (ret > 0) |
325 | continue; | 323 | continue; |
326 | 324 | ||
327 | if (gc_readl(priv, REG_GCRBEXHR) == rbexhr && | 325 | if (gc_readl(priv, REG_GCRBEXHR) == rbexhr && |
328 | priv->shared->num_interrupts == num) { | 326 | priv->shared->num_interrupts == num) { |
329 | QERROR("TIMEOUT"); | 327 | QERROR("TIMEOUT"); |
330 | ret = -ETIMEDOUT; | 328 | ret = -ETIMEDOUT; |
331 | break; | 329 | break; |
332 | } | 330 | } |
333 | } | 331 | } |
334 | 332 | ||
335 | QDUMP("done"); | 333 | QDUMP("done"); |
336 | 334 | ||
337 | return ret; | 335 | return ret; |
338 | } | 336 | } |
339 | 337 | ||
340 | static int | 338 | static int |
341 | pxa3xx_gcu_wait_free(struct pxa3xx_gcu_priv *priv) | 339 | pxa3xx_gcu_wait_free(struct pxa3xx_gcu_priv *priv) |
342 | { | 340 | { |
343 | int ret = 0; | 341 | int ret = 0; |
344 | 342 | ||
345 | QDUMP("Waiting for free..."); | 343 | QDUMP("Waiting for free..."); |
346 | 344 | ||
347 | /* Does not need to be atomic. There's a lock in user space, | 345 | /* Does not need to be atomic. There's a lock in user space, |
348 | * but anyhow, this is just for statistics. */ | 346 | * but anyhow, this is just for statistics. */ |
349 | priv->shared->num_wait_free++; | 347 | priv->shared->num_wait_free++; |
350 | 348 | ||
351 | while (!priv->free) { | 349 | while (!priv->free) { |
352 | u32 rbexhr = gc_readl(priv, REG_GCRBEXHR); | 350 | u32 rbexhr = gc_readl(priv, REG_GCRBEXHR); |
353 | 351 | ||
354 | ret = wait_event_interruptible_timeout(priv->wait_free, | 352 | ret = wait_event_interruptible_timeout(priv->wait_free, |
355 | priv->free, HZ*4); | 353 | priv->free, HZ*4); |
356 | 354 | ||
357 | if (ret < 0) | 355 | if (ret < 0) |
358 | break; | 356 | break; |
359 | 357 | ||
360 | if (ret > 0) | 358 | if (ret > 0) |
361 | continue; | 359 | continue; |
362 | 360 | ||
363 | if (gc_readl(priv, REG_GCRBEXHR) == rbexhr) { | 361 | if (gc_readl(priv, REG_GCRBEXHR) == rbexhr) { |
364 | QERROR("TIMEOUT"); | 362 | QERROR("TIMEOUT"); |
365 | ret = -ETIMEDOUT; | 363 | ret = -ETIMEDOUT; |
366 | break; | 364 | break; |
367 | } | 365 | } |
368 | } | 366 | } |
369 | 367 | ||
370 | QDUMP("done"); | 368 | QDUMP("done"); |
371 | 369 | ||
372 | return ret; | 370 | return ret; |
373 | } | 371 | } |
374 | 372 | ||
375 | /* Misc device layer */ | 373 | /* Misc device layer */ |
376 | 374 | ||
377 | static ssize_t | 375 | static ssize_t |
378 | pxa3xx_gcu_misc_write(struct file *filp, const char *buff, | 376 | pxa3xx_gcu_misc_write(struct file *filp, const char *buff, |
379 | size_t count, loff_t *offp) | 377 | size_t count, loff_t *offp) |
380 | { | 378 | { |
381 | int ret; | 379 | int ret; |
382 | unsigned long flags; | 380 | unsigned long flags; |
383 | struct pxa3xx_gcu_batch *buffer; | 381 | struct pxa3xx_gcu_batch *buffer; |
384 | struct pxa3xx_gcu_priv *priv = | 382 | struct pxa3xx_gcu_priv *priv = |
385 | container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); | 383 | container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); |
386 | 384 | ||
387 | int words = count / 4; | 385 | int words = count / 4; |
388 | 386 | ||
389 | /* Does not need to be atomic. There's a lock in user space, | 387 | /* Does not need to be atomic. There's a lock in user space, |
390 | * but anyhow, this is just for statistics. */ | 388 | * but anyhow, this is just for statistics. */ |
391 | priv->shared->num_writes++; | 389 | priv->shared->num_writes++; |
392 | 390 | ||
393 | priv->shared->num_words += words; | 391 | priv->shared->num_words += words; |
394 | 392 | ||
395 | /* Last word reserved for batch buffer end command */ | 393 | /* Last word reserved for batch buffer end command */ |
396 | if (words >= PXA3XX_GCU_BATCH_WORDS) | 394 | if (words >= PXA3XX_GCU_BATCH_WORDS) |
397 | return -E2BIG; | 395 | return -E2BIG; |
398 | 396 | ||
399 | /* Wait for a free buffer */ | 397 | /* Wait for a free buffer */ |
400 | if (!priv->free) { | 398 | if (!priv->free) { |
401 | ret = pxa3xx_gcu_wait_free(priv); | 399 | ret = pxa3xx_gcu_wait_free(priv); |
402 | if (ret < 0) | 400 | if (ret < 0) |
403 | return ret; | 401 | return ret; |
404 | } | 402 | } |
405 | 403 | ||
406 | /* | 404 | /* |
407 | * Get buffer from free list | 405 | * Get buffer from free list |
408 | */ | 406 | */ |
409 | spin_lock_irqsave(&priv->spinlock, flags); | 407 | spin_lock_irqsave(&priv->spinlock, flags); |
410 | 408 | ||
411 | buffer = priv->free; | 409 | buffer = priv->free; |
412 | priv->free = buffer->next; | 410 | priv->free = buffer->next; |
413 | 411 | ||
414 | spin_unlock_irqrestore(&priv->spinlock, flags); | 412 | spin_unlock_irqrestore(&priv->spinlock, flags); |
415 | 413 | ||
416 | 414 | ||
417 | /* Copy data from user into buffer */ | 415 | /* Copy data from user into buffer */ |
418 | ret = copy_from_user(buffer->ptr, buff, words * 4); | 416 | ret = copy_from_user(buffer->ptr, buff, words * 4); |
419 | if (ret) { | 417 | if (ret) { |
420 | spin_lock_irqsave(&priv->spinlock, flags); | 418 | spin_lock_irqsave(&priv->spinlock, flags); |
421 | buffer->next = priv->free; | 419 | buffer->next = priv->free; |
422 | priv->free = buffer; | 420 | priv->free = buffer; |
423 | spin_unlock_irqrestore(&priv->spinlock, flags); | 421 | spin_unlock_irqrestore(&priv->spinlock, flags); |
424 | return -EFAULT; | 422 | return -EFAULT; |
425 | } | 423 | } |
426 | 424 | ||
427 | buffer->length = words; | 425 | buffer->length = words; |
428 | 426 | ||
429 | /* Append batch buffer end command */ | 427 | /* Append batch buffer end command */ |
430 | buffer->ptr[words] = 0x01000000; | 428 | buffer->ptr[words] = 0x01000000; |
431 | 429 | ||
432 | /* | 430 | /* |
433 | * Add buffer to ready list | 431 | * Add buffer to ready list |
434 | */ | 432 | */ |
435 | spin_lock_irqsave(&priv->spinlock, flags); | 433 | spin_lock_irqsave(&priv->spinlock, flags); |
436 | 434 | ||
437 | buffer->next = NULL; | 435 | buffer->next = NULL; |
438 | 436 | ||
439 | if (priv->ready) { | 437 | if (priv->ready) { |
440 | BUG_ON(priv->ready_last == NULL); | 438 | BUG_ON(priv->ready_last == NULL); |
441 | 439 | ||
442 | priv->ready_last->next = buffer; | 440 | priv->ready_last->next = buffer; |
443 | } else | 441 | } else |
444 | priv->ready = buffer; | 442 | priv->ready = buffer; |
445 | 443 | ||
446 | priv->ready_last = buffer; | 444 | priv->ready_last = buffer; |
447 | 445 | ||
448 | if (!priv->shared->hw_running) | 446 | if (!priv->shared->hw_running) |
449 | run_ready(priv); | 447 | run_ready(priv); |
450 | 448 | ||
451 | spin_unlock_irqrestore(&priv->spinlock, flags); | 449 | spin_unlock_irqrestore(&priv->spinlock, flags); |
452 | 450 | ||
453 | return words * 4; | 451 | return words * 4; |
454 | } | 452 | } |
455 | 453 | ||
456 | 454 | ||
457 | static long | 455 | static long |
458 | pxa3xx_gcu_misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 456 | pxa3xx_gcu_misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
459 | { | 457 | { |
460 | unsigned long flags; | 458 | unsigned long flags; |
461 | struct pxa3xx_gcu_priv *priv = | 459 | struct pxa3xx_gcu_priv *priv = |
462 | container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); | 460 | container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); |
463 | 461 | ||
464 | switch (cmd) { | 462 | switch (cmd) { |
465 | case PXA3XX_GCU_IOCTL_RESET: | 463 | case PXA3XX_GCU_IOCTL_RESET: |
466 | spin_lock_irqsave(&priv->spinlock, flags); | 464 | spin_lock_irqsave(&priv->spinlock, flags); |
467 | pxa3xx_gcu_reset(priv); | 465 | pxa3xx_gcu_reset(priv); |
468 | spin_unlock_irqrestore(&priv->spinlock, flags); | 466 | spin_unlock_irqrestore(&priv->spinlock, flags); |
469 | return 0; | 467 | return 0; |
470 | 468 | ||
471 | case PXA3XX_GCU_IOCTL_WAIT_IDLE: | 469 | case PXA3XX_GCU_IOCTL_WAIT_IDLE: |
472 | return pxa3xx_gcu_wait_idle(priv); | 470 | return pxa3xx_gcu_wait_idle(priv); |
473 | } | 471 | } |
474 | 472 | ||
475 | return -ENOSYS; | 473 | return -ENOSYS; |
476 | } | 474 | } |
477 | 475 | ||
478 | static int | 476 | static int |
479 | pxa3xx_gcu_misc_mmap(struct file *filp, struct vm_area_struct *vma) | 477 | pxa3xx_gcu_misc_mmap(struct file *filp, struct vm_area_struct *vma) |
480 | { | 478 | { |
481 | unsigned int size = vma->vm_end - vma->vm_start; | 479 | unsigned int size = vma->vm_end - vma->vm_start; |
482 | struct pxa3xx_gcu_priv *priv = | 480 | struct pxa3xx_gcu_priv *priv = |
483 | container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); | 481 | container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); |
484 | 482 | ||
485 | switch (vma->vm_pgoff) { | 483 | switch (vma->vm_pgoff) { |
486 | case 0: | 484 | case 0: |
487 | /* hand out the shared data area */ | 485 | /* hand out the shared data area */ |
488 | if (size != SHARED_SIZE) | 486 | if (size != SHARED_SIZE) |
489 | return -EINVAL; | 487 | return -EINVAL; |
490 | 488 | ||
491 | return dma_mmap_coherent(NULL, vma, | 489 | return dma_mmap_coherent(NULL, vma, |
492 | priv->shared, priv->shared_phys, size); | 490 | priv->shared, priv->shared_phys, size); |
493 | 491 | ||
494 | case SHARED_SIZE >> PAGE_SHIFT: | 492 | case SHARED_SIZE >> PAGE_SHIFT: |
495 | /* hand out the MMIO base for direct register access | 493 | /* hand out the MMIO base for direct register access |
496 | * from userspace */ | 494 | * from userspace */ |
497 | if (size != resource_size(priv->resource_mem)) | 495 | if (size != resource_size(priv->resource_mem)) |
498 | return -EINVAL; | 496 | return -EINVAL; |
499 | 497 | ||
500 | vma->vm_flags |= VM_IO; | 498 | vma->vm_flags |= VM_IO; |
501 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | 499 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); |
502 | 500 | ||
503 | return io_remap_pfn_range(vma, vma->vm_start, | 501 | return io_remap_pfn_range(vma, vma->vm_start, |
504 | priv->resource_mem->start >> PAGE_SHIFT, | 502 | priv->resource_mem->start >> PAGE_SHIFT, |
505 | size, vma->vm_page_prot); | 503 | size, vma->vm_page_prot); |
506 | } | 504 | } |
507 | 505 | ||
508 | return -EINVAL; | 506 | return -EINVAL; |
509 | } | 507 | } |
510 | 508 | ||
511 | 509 | ||
512 | #ifdef PXA3XX_GCU_DEBUG_TIMER | 510 | #ifdef PXA3XX_GCU_DEBUG_TIMER |
513 | static struct timer_list pxa3xx_gcu_debug_timer; | 511 | static struct timer_list pxa3xx_gcu_debug_timer; |
514 | 512 | ||
515 | static void pxa3xx_gcu_debug_timedout(unsigned long ptr) | 513 | static void pxa3xx_gcu_debug_timedout(unsigned long ptr) |
516 | { | 514 | { |
517 | struct pxa3xx_gcu_priv *priv = (struct pxa3xx_gcu_priv *) ptr; | 515 | struct pxa3xx_gcu_priv *priv = (struct pxa3xx_gcu_priv *) ptr; |
518 | 516 | ||
519 | QERROR("Timer DUMP"); | 517 | QERROR("Timer DUMP"); |
520 | 518 | ||
521 | /* init the timer structure */ | 519 | /* init the timer structure */ |
522 | init_timer(&pxa3xx_gcu_debug_timer); | 520 | init_timer(&pxa3xx_gcu_debug_timer); |
523 | pxa3xx_gcu_debug_timer.function = pxa3xx_gcu_debug_timedout; | 521 | pxa3xx_gcu_debug_timer.function = pxa3xx_gcu_debug_timedout; |
524 | pxa3xx_gcu_debug_timer.data = ptr; | 522 | pxa3xx_gcu_debug_timer.data = ptr; |
525 | pxa3xx_gcu_debug_timer.expires = jiffies + 5*HZ; /* one second */ | 523 | pxa3xx_gcu_debug_timer.expires = jiffies + 5*HZ; /* one second */ |
526 | 524 | ||
527 | add_timer(&pxa3xx_gcu_debug_timer); | 525 | add_timer(&pxa3xx_gcu_debug_timer); |
528 | } | 526 | } |
529 | 527 | ||
530 | static void pxa3xx_gcu_init_debug_timer(void) | 528 | static void pxa3xx_gcu_init_debug_timer(void) |
531 | { | 529 | { |
532 | pxa3xx_gcu_debug_timedout((unsigned long) &pxa3xx_gcu_debug_timer); | 530 | pxa3xx_gcu_debug_timedout((unsigned long) &pxa3xx_gcu_debug_timer); |
533 | } | 531 | } |
534 | #else | 532 | #else |
535 | static inline void pxa3xx_gcu_init_debug_timer(void) {} | 533 | static inline void pxa3xx_gcu_init_debug_timer(void) {} |
536 | #endif | 534 | #endif |
537 | 535 | ||
538 | static int | 536 | static int |
539 | add_buffer(struct platform_device *dev, | 537 | add_buffer(struct platform_device *dev, |
540 | struct pxa3xx_gcu_priv *priv) | 538 | struct pxa3xx_gcu_priv *priv) |
541 | { | 539 | { |
542 | struct pxa3xx_gcu_batch *buffer; | 540 | struct pxa3xx_gcu_batch *buffer; |
543 | 541 | ||
544 | buffer = kzalloc(sizeof(struct pxa3xx_gcu_batch), GFP_KERNEL); | 542 | buffer = kzalloc(sizeof(struct pxa3xx_gcu_batch), GFP_KERNEL); |
545 | if (!buffer) | 543 | if (!buffer) |
546 | return -ENOMEM; | 544 | return -ENOMEM; |
547 | 545 | ||
548 | buffer->ptr = dma_alloc_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4, | 546 | buffer->ptr = dma_alloc_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4, |
549 | &buffer->phys, GFP_KERNEL); | 547 | &buffer->phys, GFP_KERNEL); |
550 | if (!buffer->ptr) { | 548 | if (!buffer->ptr) { |
551 | kfree(buffer); | 549 | kfree(buffer); |
552 | return -ENOMEM; | 550 | return -ENOMEM; |
553 | } | 551 | } |
554 | 552 | ||
555 | buffer->next = priv->free; | 553 | buffer->next = priv->free; |
556 | 554 | ||
557 | priv->free = buffer; | 555 | priv->free = buffer; |
558 | 556 | ||
559 | return 0; | 557 | return 0; |
560 | } | 558 | } |
561 | 559 | ||
562 | static void | 560 | static void |
563 | free_buffers(struct platform_device *dev, | 561 | free_buffers(struct platform_device *dev, |
564 | struct pxa3xx_gcu_priv *priv) | 562 | struct pxa3xx_gcu_priv *priv) |
565 | { | 563 | { |
566 | struct pxa3xx_gcu_batch *next, *buffer = priv->free; | 564 | struct pxa3xx_gcu_batch *next, *buffer = priv->free; |
567 | 565 | ||
568 | while (buffer) { | 566 | while (buffer) { |
569 | next = buffer->next; | 567 | next = buffer->next; |
570 | 568 | ||
571 | dma_free_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4, | 569 | dma_free_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4, |
572 | buffer->ptr, buffer->phys); | 570 | buffer->ptr, buffer->phys); |
573 | 571 | ||
574 | kfree(buffer); | 572 | kfree(buffer); |
575 | 573 | ||
576 | buffer = next; | 574 | buffer = next; |
577 | } | 575 | } |
578 | 576 | ||
579 | priv->free = NULL; | 577 | priv->free = NULL; |
580 | } | 578 | } |
581 | 579 | ||
582 | static int __devinit | 580 | static int __devinit |
583 | pxa3xx_gcu_probe(struct platform_device *dev) | 581 | pxa3xx_gcu_probe(struct platform_device *dev) |
584 | { | 582 | { |
585 | int i, ret, irq; | 583 | int i, ret, irq; |
586 | struct resource *r; | 584 | struct resource *r; |
587 | struct pxa3xx_gcu_priv *priv; | 585 | struct pxa3xx_gcu_priv *priv; |
588 | 586 | ||
589 | priv = kzalloc(sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL); | 587 | priv = kzalloc(sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL); |
590 | if (!priv) | 588 | if (!priv) |
591 | return -ENOMEM; | 589 | return -ENOMEM; |
592 | 590 | ||
593 | for (i = 0; i < 8; i++) { | 591 | for (i = 0; i < 8; i++) { |
594 | ret = add_buffer(dev, priv); | 592 | ret = add_buffer(dev, priv); |
595 | if (ret) { | 593 | if (ret) { |
596 | dev_err(&dev->dev, "failed to allocate DMA memory\n"); | 594 | dev_err(&dev->dev, "failed to allocate DMA memory\n"); |
597 | goto err_free_priv; | 595 | goto err_free_priv; |
598 | } | 596 | } |
599 | } | 597 | } |
600 | 598 | ||
601 | init_waitqueue_head(&priv->wait_idle); | 599 | init_waitqueue_head(&priv->wait_idle); |
602 | init_waitqueue_head(&priv->wait_free); | 600 | init_waitqueue_head(&priv->wait_free); |
603 | spin_lock_init(&priv->spinlock); | 601 | spin_lock_init(&priv->spinlock); |
604 | 602 | ||
605 | /* we allocate the misc device structure as part of our own allocation, | 603 | /* we allocate the misc device structure as part of our own allocation, |
606 | * so we can get a pointer to our priv structure later on with | 604 | * so we can get a pointer to our priv structure later on with |
607 | * container_of(). This isn't really necessary as we have a fixed minor | 605 | * container_of(). This isn't really necessary as we have a fixed minor |
608 | * number anyway, but this is to avoid statics. */ | 606 | * number anyway, but this is to avoid statics. */ |
609 | 607 | ||
610 | priv->misc_fops.owner = THIS_MODULE; | 608 | priv->misc_fops.owner = THIS_MODULE; |
611 | priv->misc_fops.write = pxa3xx_gcu_misc_write; | 609 | priv->misc_fops.write = pxa3xx_gcu_misc_write; |
612 | priv->misc_fops.unlocked_ioctl = pxa3xx_gcu_misc_ioctl; | 610 | priv->misc_fops.unlocked_ioctl = pxa3xx_gcu_misc_ioctl; |
613 | priv->misc_fops.mmap = pxa3xx_gcu_misc_mmap; | 611 | priv->misc_fops.mmap = pxa3xx_gcu_misc_mmap; |
614 | 612 | ||
615 | priv->misc_dev.minor = MISCDEV_MINOR, | 613 | priv->misc_dev.minor = MISCDEV_MINOR, |
616 | priv->misc_dev.name = DRV_NAME, | 614 | priv->misc_dev.name = DRV_NAME, |
617 | priv->misc_dev.fops = &priv->misc_fops, | 615 | priv->misc_dev.fops = &priv->misc_fops, |
618 | 616 | ||
619 | /* register misc device */ | 617 | /* register misc device */ |
620 | ret = misc_register(&priv->misc_dev); | 618 | ret = misc_register(&priv->misc_dev); |
621 | if (ret < 0) { | 619 | if (ret < 0) { |
622 | dev_err(&dev->dev, "misc_register() for minor %d failed\n", | 620 | dev_err(&dev->dev, "misc_register() for minor %d failed\n", |
623 | MISCDEV_MINOR); | 621 | MISCDEV_MINOR); |
624 | goto err_free_priv; | 622 | goto err_free_priv; |
625 | } | 623 | } |
626 | 624 | ||
627 | /* handle IO resources */ | 625 | /* handle IO resources */ |
628 | r = platform_get_resource(dev, IORESOURCE_MEM, 0); | 626 | r = platform_get_resource(dev, IORESOURCE_MEM, 0); |
629 | if (r == NULL) { | 627 | if (r == NULL) { |
630 | dev_err(&dev->dev, "no I/O memory resource defined\n"); | 628 | dev_err(&dev->dev, "no I/O memory resource defined\n"); |
631 | ret = -ENODEV; | 629 | ret = -ENODEV; |
632 | goto err_misc_deregister; | 630 | goto err_misc_deregister; |
633 | } | 631 | } |
634 | 632 | ||
635 | if (!request_mem_region(r->start, resource_size(r), dev->name)) { | 633 | if (!request_mem_region(r->start, resource_size(r), dev->name)) { |
636 | dev_err(&dev->dev, "failed to request I/O memory\n"); | 634 | dev_err(&dev->dev, "failed to request I/O memory\n"); |
637 | ret = -EBUSY; | 635 | ret = -EBUSY; |
638 | goto err_misc_deregister; | 636 | goto err_misc_deregister; |
639 | } | 637 | } |
640 | 638 | ||
641 | priv->mmio_base = ioremap_nocache(r->start, resource_size(r)); | 639 | priv->mmio_base = ioremap_nocache(r->start, resource_size(r)); |
642 | if (!priv->mmio_base) { | 640 | if (!priv->mmio_base) { |
643 | dev_err(&dev->dev, "failed to map I/O memory\n"); | 641 | dev_err(&dev->dev, "failed to map I/O memory\n"); |
644 | ret = -EBUSY; | 642 | ret = -EBUSY; |
645 | goto err_free_mem_region; | 643 | goto err_free_mem_region; |
646 | } | 644 | } |
647 | 645 | ||
648 | /* allocate dma memory */ | 646 | /* allocate dma memory */ |
649 | priv->shared = dma_alloc_coherent(&dev->dev, SHARED_SIZE, | 647 | priv->shared = dma_alloc_coherent(&dev->dev, SHARED_SIZE, |
650 | &priv->shared_phys, GFP_KERNEL); | 648 | &priv->shared_phys, GFP_KERNEL); |
651 | 649 | ||
652 | if (!priv->shared) { | 650 | if (!priv->shared) { |
653 | dev_err(&dev->dev, "failed to allocate DMA memory\n"); | 651 | dev_err(&dev->dev, "failed to allocate DMA memory\n"); |
654 | ret = -ENOMEM; | 652 | ret = -ENOMEM; |
655 | goto err_free_io; | 653 | goto err_free_io; |
656 | } | 654 | } |
657 | 655 | ||
658 | /* enable the clock */ | 656 | /* enable the clock */ |
659 | priv->clk = clk_get(&dev->dev, NULL); | 657 | priv->clk = clk_get(&dev->dev, NULL); |
660 | if (IS_ERR(priv->clk)) { | 658 | if (IS_ERR(priv->clk)) { |
661 | dev_err(&dev->dev, "failed to get clock\n"); | 659 | dev_err(&dev->dev, "failed to get clock\n"); |
662 | ret = -ENODEV; | 660 | ret = -ENODEV; |
663 | goto err_free_dma; | 661 | goto err_free_dma; |
664 | } | 662 | } |
665 | 663 | ||
666 | ret = clk_enable(priv->clk); | 664 | ret = clk_enable(priv->clk); |
667 | if (ret < 0) { | 665 | if (ret < 0) { |
668 | dev_err(&dev->dev, "failed to enable clock\n"); | 666 | dev_err(&dev->dev, "failed to enable clock\n"); |
669 | goto err_put_clk; | 667 | goto err_put_clk; |
670 | } | 668 | } |
671 | 669 | ||
672 | /* request the IRQ */ | 670 | /* request the IRQ */ |
673 | irq = platform_get_irq(dev, 0); | 671 | irq = platform_get_irq(dev, 0); |
674 | if (irq < 0) { | 672 | if (irq < 0) { |
675 | dev_err(&dev->dev, "no IRQ defined\n"); | 673 | dev_err(&dev->dev, "no IRQ defined\n"); |
676 | ret = -ENODEV; | 674 | ret = -ENODEV; |
677 | goto err_put_clk; | 675 | goto err_put_clk; |
678 | } | 676 | } |
679 | 677 | ||
680 | ret = request_irq(irq, pxa3xx_gcu_handle_irq, | 678 | ret = request_irq(irq, pxa3xx_gcu_handle_irq, |
681 | IRQF_DISABLED, DRV_NAME, priv); | 679 | IRQF_DISABLED, DRV_NAME, priv); |
682 | if (ret) { | 680 | if (ret) { |
683 | dev_err(&dev->dev, "request_irq failed\n"); | 681 | dev_err(&dev->dev, "request_irq failed\n"); |
684 | ret = -EBUSY; | 682 | ret = -EBUSY; |
685 | goto err_put_clk; | 683 | goto err_put_clk; |
686 | } | 684 | } |
687 | 685 | ||
688 | platform_set_drvdata(dev, priv); | 686 | platform_set_drvdata(dev, priv); |
689 | priv->resource_mem = r; | 687 | priv->resource_mem = r; |
690 | pxa3xx_gcu_reset(priv); | 688 | pxa3xx_gcu_reset(priv); |
691 | pxa3xx_gcu_init_debug_timer(); | 689 | pxa3xx_gcu_init_debug_timer(); |
692 | 690 | ||
693 | dev_info(&dev->dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n", | 691 | dev_info(&dev->dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n", |
694 | (void *) r->start, (void *) priv->shared_phys, | 692 | (void *) r->start, (void *) priv->shared_phys, |
695 | SHARED_SIZE, irq); | 693 | SHARED_SIZE, irq); |
696 | return 0; | 694 | return 0; |
697 | 695 | ||
698 | err_put_clk: | 696 | err_put_clk: |
699 | clk_disable(priv->clk); | 697 | clk_disable(priv->clk); |
700 | clk_put(priv->clk); | 698 | clk_put(priv->clk); |
701 | 699 | ||
702 | err_free_dma: | 700 | err_free_dma: |
703 | dma_free_coherent(&dev->dev, SHARED_SIZE, | 701 | dma_free_coherent(&dev->dev, SHARED_SIZE, |
704 | priv->shared, priv->shared_phys); | 702 | priv->shared, priv->shared_phys); |
705 | 703 | ||
706 | err_free_io: | 704 | err_free_io: |
707 | iounmap(priv->mmio_base); | 705 | iounmap(priv->mmio_base); |
708 | 706 | ||
709 | err_free_mem_region: | 707 | err_free_mem_region: |
710 | release_mem_region(r->start, resource_size(r)); | 708 | release_mem_region(r->start, resource_size(r)); |
711 | 709 | ||
712 | err_misc_deregister: | 710 | err_misc_deregister: |
713 | misc_deregister(&priv->misc_dev); | 711 | misc_deregister(&priv->misc_dev); |
714 | 712 | ||
715 | err_free_priv: | 713 | err_free_priv: |
716 | platform_set_drvdata(dev, NULL); | 714 | platform_set_drvdata(dev, NULL); |
717 | free_buffers(dev, priv); | 715 | free_buffers(dev, priv); |
718 | kfree(priv); | 716 | kfree(priv); |
719 | return ret; | 717 | return ret; |
720 | } | 718 | } |
721 | 719 | ||
722 | static int __devexit | 720 | static int __devexit |
723 | pxa3xx_gcu_remove(struct platform_device *dev) | 721 | pxa3xx_gcu_remove(struct platform_device *dev) |
724 | { | 722 | { |
725 | struct pxa3xx_gcu_priv *priv = platform_get_drvdata(dev); | 723 | struct pxa3xx_gcu_priv *priv = platform_get_drvdata(dev); |
726 | struct resource *r = priv->resource_mem; | 724 | struct resource *r = priv->resource_mem; |
727 | 725 | ||
728 | pxa3xx_gcu_wait_idle(priv); | 726 | pxa3xx_gcu_wait_idle(priv); |
729 | 727 | ||
730 | misc_deregister(&priv->misc_dev); | 728 | misc_deregister(&priv->misc_dev); |
731 | dma_free_coherent(&dev->dev, SHARED_SIZE, | 729 | dma_free_coherent(&dev->dev, SHARED_SIZE, |
732 | priv->shared, priv->shared_phys); | 730 | priv->shared, priv->shared_phys); |
733 | iounmap(priv->mmio_base); | 731 | iounmap(priv->mmio_base); |
734 | release_mem_region(r->start, resource_size(r)); | 732 | release_mem_region(r->start, resource_size(r)); |
735 | platform_set_drvdata(dev, NULL); | 733 | platform_set_drvdata(dev, NULL); |
736 | clk_disable(priv->clk); | 734 | clk_disable(priv->clk); |
737 | free_buffers(dev, priv); | 735 | free_buffers(dev, priv); |
738 | kfree(priv); | 736 | kfree(priv); |
739 | 737 | ||
740 | return 0; | 738 | return 0; |
741 | } | 739 | } |
742 | 740 | ||
743 | static struct platform_driver pxa3xx_gcu_driver = { | 741 | static struct platform_driver pxa3xx_gcu_driver = { |
744 | .probe = pxa3xx_gcu_probe, | 742 | .probe = pxa3xx_gcu_probe, |
745 | .remove = __devexit_p(pxa3xx_gcu_remove), | 743 | .remove = __devexit_p(pxa3xx_gcu_remove), |
746 | .driver = { | 744 | .driver = { |
747 | .owner = THIS_MODULE, | 745 | .owner = THIS_MODULE, |
748 | .name = DRV_NAME, | 746 | .name = DRV_NAME, |
749 | }, | 747 | }, |
750 | }; | 748 | }; |
751 | 749 | ||
752 | static int __init | 750 | static int __init |
753 | pxa3xx_gcu_init(void) | 751 | pxa3xx_gcu_init(void) |
754 | { | 752 | { |
755 | return platform_driver_register(&pxa3xx_gcu_driver); | 753 | return platform_driver_register(&pxa3xx_gcu_driver); |
756 | } | 754 | } |
757 | 755 | ||
758 | static void __exit | 756 | static void __exit |
759 | pxa3xx_gcu_exit(void) | 757 | pxa3xx_gcu_exit(void) |
760 | { | 758 | { |
761 | platform_driver_unregister(&pxa3xx_gcu_driver); | 759 | platform_driver_unregister(&pxa3xx_gcu_driver); |
762 | } | 760 | } |
763 | 761 | ||
764 | module_init(pxa3xx_gcu_init); | 762 | module_init(pxa3xx_gcu_init); |
765 | module_exit(pxa3xx_gcu_exit); | 763 | module_exit(pxa3xx_gcu_exit); |
766 | 764 | ||
767 | MODULE_DESCRIPTION("PXA3xx graphics controller unit driver"); | 765 | MODULE_DESCRIPTION("PXA3xx graphics controller unit driver"); |
768 | MODULE_LICENSE("GPL"); | 766 | MODULE_LICENSE("GPL"); |
769 | MODULE_ALIAS_MISCDEV(MISCDEV_MINOR); | 767 | MODULE_ALIAS_MISCDEV(MISCDEV_MINOR); |
770 | MODULE_AUTHOR("Janine Kropp <nin@directfb.org>, " | 768 | MODULE_AUTHOR("Janine Kropp <nin@directfb.org>, " |
771 | "Denis Oliver Kropp <dok@directfb.org>, " | 769 | "Denis Oliver Kropp <dok@directfb.org>, " |
772 | "Daniel Mack <daniel@caiaq.de>"); | 770 | "Daniel Mack <daniel@caiaq.de>"); |
773 | 771 |
drivers/video/xilinxfb.c
1 | /* | 1 | /* |
2 | * Xilinx TFT frame buffer driver | 2 | * Xilinx TFT frame buffer driver |
3 | * | 3 | * |
4 | * Author: MontaVista Software, Inc. | 4 | * Author: MontaVista Software, Inc. |
5 | * source@mvista.com | 5 | * source@mvista.com |
6 | * | 6 | * |
7 | * 2002-2007 (c) MontaVista Software, Inc. | 7 | * 2002-2007 (c) MontaVista Software, Inc. |
8 | * 2007 (c) Secret Lab Technologies, Ltd. | 8 | * 2007 (c) Secret Lab Technologies, Ltd. |
9 | * 2009 (c) Xilinx Inc. | 9 | * 2009 (c) Xilinx Inc. |
10 | * | 10 | * |
11 | * This file is licensed under the terms of the GNU General Public License | 11 | * This file is licensed under the terms of the GNU General Public License |
12 | * version 2. This program is licensed "as is" without any warranty of any | 12 | * version 2. This program is licensed "as is" without any warranty of any |
13 | * kind, whether express or implied. | 13 | * kind, whether express or implied. |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* | 16 | /* |
17 | * This driver was based on au1100fb.c by MontaVista rewritten for 2.6 | 17 | * This driver was based on au1100fb.c by MontaVista rewritten for 2.6 |
18 | * by Embedded Alley Solutions <source@embeddedalley.com>, which in turn | 18 | * by Embedded Alley Solutions <source@embeddedalley.com>, which in turn |
19 | * was based on skeletonfb.c, Skeleton for a frame buffer device by | 19 | * was based on skeletonfb.c, Skeleton for a frame buffer device by |
20 | * Geert Uytterhoeven. | 20 | * Geert Uytterhoeven. |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <linux/device.h> | 23 | #include <linux/device.h> |
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
26 | #include <linux/version.h> | ||
27 | #include <linux/errno.h> | 26 | #include <linux/errno.h> |
28 | #include <linux/string.h> | 27 | #include <linux/string.h> |
29 | #include <linux/mm.h> | 28 | #include <linux/mm.h> |
30 | #include <linux/fb.h> | 29 | #include <linux/fb.h> |
31 | #include <linux/init.h> | 30 | #include <linux/init.h> |
32 | #include <linux/dma-mapping.h> | 31 | #include <linux/dma-mapping.h> |
33 | #include <linux/of_device.h> | 32 | #include <linux/of_device.h> |
34 | #include <linux/of_platform.h> | 33 | #include <linux/of_platform.h> |
35 | #include <linux/of_address.h> | 34 | #include <linux/of_address.h> |
36 | #include <linux/io.h> | 35 | #include <linux/io.h> |
37 | #include <linux/xilinxfb.h> | 36 | #include <linux/xilinxfb.h> |
38 | #include <linux/slab.h> | 37 | #include <linux/slab.h> |
39 | 38 | ||
40 | #ifdef CONFIG_PPC_DCR | 39 | #ifdef CONFIG_PPC_DCR |
41 | #include <asm/dcr.h> | 40 | #include <asm/dcr.h> |
42 | #endif | 41 | #endif |
43 | 42 | ||
44 | #define DRIVER_NAME "xilinxfb" | 43 | #define DRIVER_NAME "xilinxfb" |
45 | 44 | ||
46 | 45 | ||
47 | /* | 46 | /* |
48 | * Xilinx calls it "PLB TFT LCD Controller" though it can also be used for | 47 | * Xilinx calls it "PLB TFT LCD Controller" though it can also be used for |
49 | * the VGA port on the Xilinx ML40x board. This is a hardware display | 48 | * the VGA port on the Xilinx ML40x board. This is a hardware display |
50 | * controller for a 640x480 resolution TFT or VGA screen. | 49 | * controller for a 640x480 resolution TFT or VGA screen. |
51 | * | 50 | * |
52 | * The interface to the framebuffer is nice and simple. There are two | 51 | * The interface to the framebuffer is nice and simple. There are two |
53 | * control registers. The first tells the LCD interface where in memory | 52 | * control registers. The first tells the LCD interface where in memory |
54 | * the frame buffer is (only the 11 most significant bits are used, so | 53 | * the frame buffer is (only the 11 most significant bits are used, so |
55 | * don't start thinking about scrolling). The second allows the LCD to | 54 | * don't start thinking about scrolling). The second allows the LCD to |
56 | * be turned on or off as well as rotated 180 degrees. | 55 | * be turned on or off as well as rotated 180 degrees. |
57 | * | 56 | * |
58 | * In case of direct PLB access the second control register will be at | 57 | * In case of direct PLB access the second control register will be at |
59 | * an offset of 4 as compared to the DCR access where the offset is 1 | 58 | * an offset of 4 as compared to the DCR access where the offset is 1 |
60 | * i.e. REG_CTRL. So this is taken care in the function | 59 | * i.e. REG_CTRL. So this is taken care in the function |
61 | * xilinx_fb_out_be32 where it left shifts the offset 2 times in case of | 60 | * xilinx_fb_out_be32 where it left shifts the offset 2 times in case of |
62 | * direct PLB access. | 61 | * direct PLB access. |
63 | */ | 62 | */ |
64 | #define NUM_REGS 2 | 63 | #define NUM_REGS 2 |
65 | #define REG_FB_ADDR 0 | 64 | #define REG_FB_ADDR 0 |
66 | #define REG_CTRL 1 | 65 | #define REG_CTRL 1 |
67 | #define REG_CTRL_ENABLE 0x0001 | 66 | #define REG_CTRL_ENABLE 0x0001 |
68 | #define REG_CTRL_ROTATE 0x0002 | 67 | #define REG_CTRL_ROTATE 0x0002 |
69 | 68 | ||
70 | /* | 69 | /* |
71 | * The hardware only handles a single mode: 640x480 24 bit true | 70 | * The hardware only handles a single mode: 640x480 24 bit true |
72 | * color. Each pixel gets a word (32 bits) of memory. Within each word, | 71 | * color. Each pixel gets a word (32 bits) of memory. Within each word, |
73 | * the 8 most significant bits are ignored, the next 8 bits are the red | 72 | * the 8 most significant bits are ignored, the next 8 bits are the red |
74 | * level, the next 8 bits are the green level and the 8 least | 73 | * level, the next 8 bits are the green level and the 8 least |
75 | * significant bits are the blue level. Each row of the LCD uses 1024 | 74 | * significant bits are the blue level. Each row of the LCD uses 1024 |
76 | * words, but only the first 640 pixels are displayed with the other 384 | 75 | * words, but only the first 640 pixels are displayed with the other 384 |
77 | * words being ignored. There are 480 rows. | 76 | * words being ignored. There are 480 rows. |
78 | */ | 77 | */ |
79 | #define BYTES_PER_PIXEL 4 | 78 | #define BYTES_PER_PIXEL 4 |
80 | #define BITS_PER_PIXEL (BYTES_PER_PIXEL * 8) | 79 | #define BITS_PER_PIXEL (BYTES_PER_PIXEL * 8) |
81 | 80 | ||
82 | #define RED_SHIFT 16 | 81 | #define RED_SHIFT 16 |
83 | #define GREEN_SHIFT 8 | 82 | #define GREEN_SHIFT 8 |
84 | #define BLUE_SHIFT 0 | 83 | #define BLUE_SHIFT 0 |
85 | 84 | ||
86 | #define PALETTE_ENTRIES_NO 16 /* passed to fb_alloc_cmap() */ | 85 | #define PALETTE_ENTRIES_NO 16 /* passed to fb_alloc_cmap() */ |
87 | 86 | ||
88 | /* | 87 | /* |
89 | * Default xilinxfb configuration | 88 | * Default xilinxfb configuration |
90 | */ | 89 | */ |
91 | static struct xilinxfb_platform_data xilinx_fb_default_pdata = { | 90 | static struct xilinxfb_platform_data xilinx_fb_default_pdata = { |
92 | .xres = 640, | 91 | .xres = 640, |
93 | .yres = 480, | 92 | .yres = 480, |
94 | .xvirt = 1024, | 93 | .xvirt = 1024, |
95 | .yvirt = 480, | 94 | .yvirt = 480, |
96 | }; | 95 | }; |
97 | 96 | ||
98 | /* | 97 | /* |
99 | * Here are the default fb_fix_screeninfo and fb_var_screeninfo structures | 98 | * Here are the default fb_fix_screeninfo and fb_var_screeninfo structures |
100 | */ | 99 | */ |
101 | static struct fb_fix_screeninfo xilinx_fb_fix = { | 100 | static struct fb_fix_screeninfo xilinx_fb_fix = { |
102 | .id = "Xilinx", | 101 | .id = "Xilinx", |
103 | .type = FB_TYPE_PACKED_PIXELS, | 102 | .type = FB_TYPE_PACKED_PIXELS, |
104 | .visual = FB_VISUAL_TRUECOLOR, | 103 | .visual = FB_VISUAL_TRUECOLOR, |
105 | .accel = FB_ACCEL_NONE | 104 | .accel = FB_ACCEL_NONE |
106 | }; | 105 | }; |
107 | 106 | ||
108 | static struct fb_var_screeninfo xilinx_fb_var = { | 107 | static struct fb_var_screeninfo xilinx_fb_var = { |
109 | .bits_per_pixel = BITS_PER_PIXEL, | 108 | .bits_per_pixel = BITS_PER_PIXEL, |
110 | 109 | ||
111 | .red = { RED_SHIFT, 8, 0 }, | 110 | .red = { RED_SHIFT, 8, 0 }, |
112 | .green = { GREEN_SHIFT, 8, 0 }, | 111 | .green = { GREEN_SHIFT, 8, 0 }, |
113 | .blue = { BLUE_SHIFT, 8, 0 }, | 112 | .blue = { BLUE_SHIFT, 8, 0 }, |
114 | .transp = { 0, 0, 0 }, | 113 | .transp = { 0, 0, 0 }, |
115 | 114 | ||
116 | .activate = FB_ACTIVATE_NOW | 115 | .activate = FB_ACTIVATE_NOW |
117 | }; | 116 | }; |
118 | 117 | ||
119 | 118 | ||
120 | #define PLB_ACCESS_FLAG 0x1 /* 1 = PLB, 0 = DCR */ | 119 | #define PLB_ACCESS_FLAG 0x1 /* 1 = PLB, 0 = DCR */ |
121 | 120 | ||
122 | struct xilinxfb_drvdata { | 121 | struct xilinxfb_drvdata { |
123 | 122 | ||
124 | struct fb_info info; /* FB driver info record */ | 123 | struct fb_info info; /* FB driver info record */ |
125 | 124 | ||
126 | phys_addr_t regs_phys; /* phys. address of the control | 125 | phys_addr_t regs_phys; /* phys. address of the control |
127 | registers */ | 126 | registers */ |
128 | void __iomem *regs; /* virt. address of the control | 127 | void __iomem *regs; /* virt. address of the control |
129 | registers */ | 128 | registers */ |
130 | #ifdef CONFIG_PPC_DCR | 129 | #ifdef CONFIG_PPC_DCR |
131 | dcr_host_t dcr_host; | 130 | dcr_host_t dcr_host; |
132 | unsigned int dcr_len; | 131 | unsigned int dcr_len; |
133 | #endif | 132 | #endif |
134 | void *fb_virt; /* virt. address of the frame buffer */ | 133 | void *fb_virt; /* virt. address of the frame buffer */ |
135 | dma_addr_t fb_phys; /* phys. address of the frame buffer */ | 134 | dma_addr_t fb_phys; /* phys. address of the frame buffer */ |
136 | int fb_alloced; /* Flag, was the fb memory alloced? */ | 135 | int fb_alloced; /* Flag, was the fb memory alloced? */ |
137 | 136 | ||
138 | u8 flags; /* features of the driver */ | 137 | u8 flags; /* features of the driver */ |
139 | 138 | ||
140 | u32 reg_ctrl_default; | 139 | u32 reg_ctrl_default; |
141 | 140 | ||
142 | u32 pseudo_palette[PALETTE_ENTRIES_NO]; | 141 | u32 pseudo_palette[PALETTE_ENTRIES_NO]; |
143 | /* Fake palette of 16 colors */ | 142 | /* Fake palette of 16 colors */ |
144 | }; | 143 | }; |
145 | 144 | ||
146 | #define to_xilinxfb_drvdata(_info) \ | 145 | #define to_xilinxfb_drvdata(_info) \ |
147 | container_of(_info, struct xilinxfb_drvdata, info) | 146 | container_of(_info, struct xilinxfb_drvdata, info) |
148 | 147 | ||
149 | /* | 148 | /* |
150 | * The XPS TFT Controller can be accessed through PLB or DCR interface. | 149 | * The XPS TFT Controller can be accessed through PLB or DCR interface. |
151 | * To perform the read/write on the registers we need to check on | 150 | * To perform the read/write on the registers we need to check on |
152 | * which bus its connected and call the appropriate write API. | 151 | * which bus its connected and call the appropriate write API. |
153 | */ | 152 | */ |
154 | static void xilinx_fb_out_be32(struct xilinxfb_drvdata *drvdata, u32 offset, | 153 | static void xilinx_fb_out_be32(struct xilinxfb_drvdata *drvdata, u32 offset, |
155 | u32 val) | 154 | u32 val) |
156 | { | 155 | { |
157 | if (drvdata->flags & PLB_ACCESS_FLAG) | 156 | if (drvdata->flags & PLB_ACCESS_FLAG) |
158 | out_be32(drvdata->regs + (offset << 2), val); | 157 | out_be32(drvdata->regs + (offset << 2), val); |
159 | #ifdef CONFIG_PPC_DCR | 158 | #ifdef CONFIG_PPC_DCR |
160 | else | 159 | else |
161 | dcr_write(drvdata->dcr_host, offset, val); | 160 | dcr_write(drvdata->dcr_host, offset, val); |
162 | #endif | 161 | #endif |
163 | } | 162 | } |
164 | 163 | ||
165 | static int | 164 | static int |
166 | xilinx_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, | 165 | xilinx_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, |
167 | unsigned transp, struct fb_info *fbi) | 166 | unsigned transp, struct fb_info *fbi) |
168 | { | 167 | { |
169 | u32 *palette = fbi->pseudo_palette; | 168 | u32 *palette = fbi->pseudo_palette; |
170 | 169 | ||
171 | if (regno >= PALETTE_ENTRIES_NO) | 170 | if (regno >= PALETTE_ENTRIES_NO) |
172 | return -EINVAL; | 171 | return -EINVAL; |
173 | 172 | ||
174 | if (fbi->var.grayscale) { | 173 | if (fbi->var.grayscale) { |
175 | /* Convert color to grayscale. | 174 | /* Convert color to grayscale. |
176 | * grayscale = 0.30*R + 0.59*G + 0.11*B */ | 175 | * grayscale = 0.30*R + 0.59*G + 0.11*B */ |
177 | red = green = blue = | 176 | red = green = blue = |
178 | (red * 77 + green * 151 + blue * 28 + 127) >> 8; | 177 | (red * 77 + green * 151 + blue * 28 + 127) >> 8; |
179 | } | 178 | } |
180 | 179 | ||
181 | /* fbi->fix.visual is always FB_VISUAL_TRUECOLOR */ | 180 | /* fbi->fix.visual is always FB_VISUAL_TRUECOLOR */ |
182 | 181 | ||
183 | /* We only handle 8 bits of each color. */ | 182 | /* We only handle 8 bits of each color. */ |
184 | red >>= 8; | 183 | red >>= 8; |
185 | green >>= 8; | 184 | green >>= 8; |
186 | blue >>= 8; | 185 | blue >>= 8; |
187 | palette[regno] = (red << RED_SHIFT) | (green << GREEN_SHIFT) | | 186 | palette[regno] = (red << RED_SHIFT) | (green << GREEN_SHIFT) | |
188 | (blue << BLUE_SHIFT); | 187 | (blue << BLUE_SHIFT); |
189 | 188 | ||
190 | return 0; | 189 | return 0; |
191 | } | 190 | } |
192 | 191 | ||
193 | static int | 192 | static int |
194 | xilinx_fb_blank(int blank_mode, struct fb_info *fbi) | 193 | xilinx_fb_blank(int blank_mode, struct fb_info *fbi) |
195 | { | 194 | { |
196 | struct xilinxfb_drvdata *drvdata = to_xilinxfb_drvdata(fbi); | 195 | struct xilinxfb_drvdata *drvdata = to_xilinxfb_drvdata(fbi); |
197 | 196 | ||
198 | switch (blank_mode) { | 197 | switch (blank_mode) { |
199 | case FB_BLANK_UNBLANK: | 198 | case FB_BLANK_UNBLANK: |
200 | /* turn on panel */ | 199 | /* turn on panel */ |
201 | xilinx_fb_out_be32(drvdata, REG_CTRL, drvdata->reg_ctrl_default); | 200 | xilinx_fb_out_be32(drvdata, REG_CTRL, drvdata->reg_ctrl_default); |
202 | break; | 201 | break; |
203 | 202 | ||
204 | case FB_BLANK_NORMAL: | 203 | case FB_BLANK_NORMAL: |
205 | case FB_BLANK_VSYNC_SUSPEND: | 204 | case FB_BLANK_VSYNC_SUSPEND: |
206 | case FB_BLANK_HSYNC_SUSPEND: | 205 | case FB_BLANK_HSYNC_SUSPEND: |
207 | case FB_BLANK_POWERDOWN: | 206 | case FB_BLANK_POWERDOWN: |
208 | /* turn off panel */ | 207 | /* turn off panel */ |
209 | xilinx_fb_out_be32(drvdata, REG_CTRL, 0); | 208 | xilinx_fb_out_be32(drvdata, REG_CTRL, 0); |
210 | default: | 209 | default: |
211 | break; | 210 | break; |
212 | 211 | ||
213 | } | 212 | } |
214 | return 0; /* success */ | 213 | return 0; /* success */ |
215 | } | 214 | } |
216 | 215 | ||
217 | static struct fb_ops xilinxfb_ops = | 216 | static struct fb_ops xilinxfb_ops = |
218 | { | 217 | { |
219 | .owner = THIS_MODULE, | 218 | .owner = THIS_MODULE, |
220 | .fb_setcolreg = xilinx_fb_setcolreg, | 219 | .fb_setcolreg = xilinx_fb_setcolreg, |
221 | .fb_blank = xilinx_fb_blank, | 220 | .fb_blank = xilinx_fb_blank, |
222 | .fb_fillrect = cfb_fillrect, | 221 | .fb_fillrect = cfb_fillrect, |
223 | .fb_copyarea = cfb_copyarea, | 222 | .fb_copyarea = cfb_copyarea, |
224 | .fb_imageblit = cfb_imageblit, | 223 | .fb_imageblit = cfb_imageblit, |
225 | }; | 224 | }; |
226 | 225 | ||
227 | /* --------------------------------------------------------------------- | 226 | /* --------------------------------------------------------------------- |
228 | * Bus independent setup/teardown | 227 | * Bus independent setup/teardown |
229 | */ | 228 | */ |
230 | 229 | ||
231 | static int xilinxfb_assign(struct device *dev, | 230 | static int xilinxfb_assign(struct device *dev, |
232 | struct xilinxfb_drvdata *drvdata, | 231 | struct xilinxfb_drvdata *drvdata, |
233 | unsigned long physaddr, | 232 | unsigned long physaddr, |
234 | struct xilinxfb_platform_data *pdata) | 233 | struct xilinxfb_platform_data *pdata) |
235 | { | 234 | { |
236 | int rc; | 235 | int rc; |
237 | int fbsize = pdata->xvirt * pdata->yvirt * BYTES_PER_PIXEL; | 236 | int fbsize = pdata->xvirt * pdata->yvirt * BYTES_PER_PIXEL; |
238 | 237 | ||
239 | if (drvdata->flags & PLB_ACCESS_FLAG) { | 238 | if (drvdata->flags & PLB_ACCESS_FLAG) { |
240 | /* | 239 | /* |
241 | * Map the control registers in if the controller | 240 | * Map the control registers in if the controller |
242 | * is on direct PLB interface. | 241 | * is on direct PLB interface. |
243 | */ | 242 | */ |
244 | if (!request_mem_region(physaddr, 8, DRIVER_NAME)) { | 243 | if (!request_mem_region(physaddr, 8, DRIVER_NAME)) { |
245 | dev_err(dev, "Couldn't lock memory region at 0x%08lX\n", | 244 | dev_err(dev, "Couldn't lock memory region at 0x%08lX\n", |
246 | physaddr); | 245 | physaddr); |
247 | rc = -ENODEV; | 246 | rc = -ENODEV; |
248 | goto err_region; | 247 | goto err_region; |
249 | } | 248 | } |
250 | 249 | ||
251 | drvdata->regs_phys = physaddr; | 250 | drvdata->regs_phys = physaddr; |
252 | drvdata->regs = ioremap(physaddr, 8); | 251 | drvdata->regs = ioremap(physaddr, 8); |
253 | if (!drvdata->regs) { | 252 | if (!drvdata->regs) { |
254 | dev_err(dev, "Couldn't lock memory region at 0x%08lX\n", | 253 | dev_err(dev, "Couldn't lock memory region at 0x%08lX\n", |
255 | physaddr); | 254 | physaddr); |
256 | rc = -ENODEV; | 255 | rc = -ENODEV; |
257 | goto err_map; | 256 | goto err_map; |
258 | } | 257 | } |
259 | } | 258 | } |
260 | 259 | ||
261 | /* Allocate the framebuffer memory */ | 260 | /* Allocate the framebuffer memory */ |
262 | if (pdata->fb_phys) { | 261 | if (pdata->fb_phys) { |
263 | drvdata->fb_phys = pdata->fb_phys; | 262 | drvdata->fb_phys = pdata->fb_phys; |
264 | drvdata->fb_virt = ioremap(pdata->fb_phys, fbsize); | 263 | drvdata->fb_virt = ioremap(pdata->fb_phys, fbsize); |
265 | } else { | 264 | } else { |
266 | drvdata->fb_alloced = 1; | 265 | drvdata->fb_alloced = 1; |
267 | drvdata->fb_virt = dma_alloc_coherent(dev, PAGE_ALIGN(fbsize), | 266 | drvdata->fb_virt = dma_alloc_coherent(dev, PAGE_ALIGN(fbsize), |
268 | &drvdata->fb_phys, GFP_KERNEL); | 267 | &drvdata->fb_phys, GFP_KERNEL); |
269 | } | 268 | } |
270 | 269 | ||
271 | if (!drvdata->fb_virt) { | 270 | if (!drvdata->fb_virt) { |
272 | dev_err(dev, "Could not allocate frame buffer memory\n"); | 271 | dev_err(dev, "Could not allocate frame buffer memory\n"); |
273 | rc = -ENOMEM; | 272 | rc = -ENOMEM; |
274 | if (drvdata->flags & PLB_ACCESS_FLAG) | 273 | if (drvdata->flags & PLB_ACCESS_FLAG) |
275 | goto err_fbmem; | 274 | goto err_fbmem; |
276 | else | 275 | else |
277 | goto err_region; | 276 | goto err_region; |
278 | } | 277 | } |
279 | 278 | ||
280 | /* Clear (turn to black) the framebuffer */ | 279 | /* Clear (turn to black) the framebuffer */ |
281 | memset_io((void __iomem *)drvdata->fb_virt, 0, fbsize); | 280 | memset_io((void __iomem *)drvdata->fb_virt, 0, fbsize); |
282 | 281 | ||
283 | /* Tell the hardware where the frame buffer is */ | 282 | /* Tell the hardware where the frame buffer is */ |
284 | xilinx_fb_out_be32(drvdata, REG_FB_ADDR, drvdata->fb_phys); | 283 | xilinx_fb_out_be32(drvdata, REG_FB_ADDR, drvdata->fb_phys); |
285 | 284 | ||
286 | /* Turn on the display */ | 285 | /* Turn on the display */ |
287 | drvdata->reg_ctrl_default = REG_CTRL_ENABLE; | 286 | drvdata->reg_ctrl_default = REG_CTRL_ENABLE; |
288 | if (pdata->rotate_screen) | 287 | if (pdata->rotate_screen) |
289 | drvdata->reg_ctrl_default |= REG_CTRL_ROTATE; | 288 | drvdata->reg_ctrl_default |= REG_CTRL_ROTATE; |
290 | xilinx_fb_out_be32(drvdata, REG_CTRL, | 289 | xilinx_fb_out_be32(drvdata, REG_CTRL, |
291 | drvdata->reg_ctrl_default); | 290 | drvdata->reg_ctrl_default); |
292 | 291 | ||
293 | /* Fill struct fb_info */ | 292 | /* Fill struct fb_info */ |
294 | drvdata->info.device = dev; | 293 | drvdata->info.device = dev; |
295 | drvdata->info.screen_base = (void __iomem *)drvdata->fb_virt; | 294 | drvdata->info.screen_base = (void __iomem *)drvdata->fb_virt; |
296 | drvdata->info.fbops = &xilinxfb_ops; | 295 | drvdata->info.fbops = &xilinxfb_ops; |
297 | drvdata->info.fix = xilinx_fb_fix; | 296 | drvdata->info.fix = xilinx_fb_fix; |
298 | drvdata->info.fix.smem_start = drvdata->fb_phys; | 297 | drvdata->info.fix.smem_start = drvdata->fb_phys; |
299 | drvdata->info.fix.smem_len = fbsize; | 298 | drvdata->info.fix.smem_len = fbsize; |
300 | drvdata->info.fix.line_length = pdata->xvirt * BYTES_PER_PIXEL; | 299 | drvdata->info.fix.line_length = pdata->xvirt * BYTES_PER_PIXEL; |
301 | 300 | ||
302 | drvdata->info.pseudo_palette = drvdata->pseudo_palette; | 301 | drvdata->info.pseudo_palette = drvdata->pseudo_palette; |
303 | drvdata->info.flags = FBINFO_DEFAULT; | 302 | drvdata->info.flags = FBINFO_DEFAULT; |
304 | drvdata->info.var = xilinx_fb_var; | 303 | drvdata->info.var = xilinx_fb_var; |
305 | drvdata->info.var.height = pdata->screen_height_mm; | 304 | drvdata->info.var.height = pdata->screen_height_mm; |
306 | drvdata->info.var.width = pdata->screen_width_mm; | 305 | drvdata->info.var.width = pdata->screen_width_mm; |
307 | drvdata->info.var.xres = pdata->xres; | 306 | drvdata->info.var.xres = pdata->xres; |
308 | drvdata->info.var.yres = pdata->yres; | 307 | drvdata->info.var.yres = pdata->yres; |
309 | drvdata->info.var.xres_virtual = pdata->xvirt; | 308 | drvdata->info.var.xres_virtual = pdata->xvirt; |
310 | drvdata->info.var.yres_virtual = pdata->yvirt; | 309 | drvdata->info.var.yres_virtual = pdata->yvirt; |
311 | 310 | ||
312 | /* Allocate a colour map */ | 311 | /* Allocate a colour map */ |
313 | rc = fb_alloc_cmap(&drvdata->info.cmap, PALETTE_ENTRIES_NO, 0); | 312 | rc = fb_alloc_cmap(&drvdata->info.cmap, PALETTE_ENTRIES_NO, 0); |
314 | if (rc) { | 313 | if (rc) { |
315 | dev_err(dev, "Fail to allocate colormap (%d entries)\n", | 314 | dev_err(dev, "Fail to allocate colormap (%d entries)\n", |
316 | PALETTE_ENTRIES_NO); | 315 | PALETTE_ENTRIES_NO); |
317 | goto err_cmap; | 316 | goto err_cmap; |
318 | } | 317 | } |
319 | 318 | ||
320 | /* Register new frame buffer */ | 319 | /* Register new frame buffer */ |
321 | rc = register_framebuffer(&drvdata->info); | 320 | rc = register_framebuffer(&drvdata->info); |
322 | if (rc) { | 321 | if (rc) { |
323 | dev_err(dev, "Could not register frame buffer\n"); | 322 | dev_err(dev, "Could not register frame buffer\n"); |
324 | goto err_regfb; | 323 | goto err_regfb; |
325 | } | 324 | } |
326 | 325 | ||
327 | if (drvdata->flags & PLB_ACCESS_FLAG) { | 326 | if (drvdata->flags & PLB_ACCESS_FLAG) { |
328 | /* Put a banner in the log (for DEBUG) */ | 327 | /* Put a banner in the log (for DEBUG) */ |
329 | dev_dbg(dev, "regs: phys=%lx, virt=%p\n", physaddr, | 328 | dev_dbg(dev, "regs: phys=%lx, virt=%p\n", physaddr, |
330 | drvdata->regs); | 329 | drvdata->regs); |
331 | } | 330 | } |
332 | /* Put a banner in the log (for DEBUG) */ | 331 | /* Put a banner in the log (for DEBUG) */ |
333 | dev_dbg(dev, "fb: phys=%llx, virt=%p, size=%x\n", | 332 | dev_dbg(dev, "fb: phys=%llx, virt=%p, size=%x\n", |
334 | (unsigned long long)drvdata->fb_phys, drvdata->fb_virt, fbsize); | 333 | (unsigned long long)drvdata->fb_phys, drvdata->fb_virt, fbsize); |
335 | 334 | ||
336 | return 0; /* success */ | 335 | return 0; /* success */ |
337 | 336 | ||
338 | err_regfb: | 337 | err_regfb: |
339 | fb_dealloc_cmap(&drvdata->info.cmap); | 338 | fb_dealloc_cmap(&drvdata->info.cmap); |
340 | 339 | ||
341 | err_cmap: | 340 | err_cmap: |
342 | if (drvdata->fb_alloced) | 341 | if (drvdata->fb_alloced) |
343 | dma_free_coherent(dev, PAGE_ALIGN(fbsize), drvdata->fb_virt, | 342 | dma_free_coherent(dev, PAGE_ALIGN(fbsize), drvdata->fb_virt, |
344 | drvdata->fb_phys); | 343 | drvdata->fb_phys); |
345 | else | 344 | else |
346 | iounmap(drvdata->fb_virt); | 345 | iounmap(drvdata->fb_virt); |
347 | 346 | ||
348 | /* Turn off the display */ | 347 | /* Turn off the display */ |
349 | xilinx_fb_out_be32(drvdata, REG_CTRL, 0); | 348 | xilinx_fb_out_be32(drvdata, REG_CTRL, 0); |
350 | 349 | ||
351 | err_fbmem: | 350 | err_fbmem: |
352 | if (drvdata->flags & PLB_ACCESS_FLAG) | 351 | if (drvdata->flags & PLB_ACCESS_FLAG) |
353 | iounmap(drvdata->regs); | 352 | iounmap(drvdata->regs); |
354 | 353 | ||
355 | err_map: | 354 | err_map: |
356 | if (drvdata->flags & PLB_ACCESS_FLAG) | 355 | if (drvdata->flags & PLB_ACCESS_FLAG) |
357 | release_mem_region(physaddr, 8); | 356 | release_mem_region(physaddr, 8); |
358 | 357 | ||
359 | err_region: | 358 | err_region: |
360 | kfree(drvdata); | 359 | kfree(drvdata); |
361 | dev_set_drvdata(dev, NULL); | 360 | dev_set_drvdata(dev, NULL); |
362 | 361 | ||
363 | return rc; | 362 | return rc; |
364 | } | 363 | } |
365 | 364 | ||
366 | static int xilinxfb_release(struct device *dev) | 365 | static int xilinxfb_release(struct device *dev) |
367 | { | 366 | { |
368 | struct xilinxfb_drvdata *drvdata = dev_get_drvdata(dev); | 367 | struct xilinxfb_drvdata *drvdata = dev_get_drvdata(dev); |
369 | 368 | ||
370 | #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) | 369 | #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) |
371 | xilinx_fb_blank(VESA_POWERDOWN, &drvdata->info); | 370 | xilinx_fb_blank(VESA_POWERDOWN, &drvdata->info); |
372 | #endif | 371 | #endif |
373 | 372 | ||
374 | unregister_framebuffer(&drvdata->info); | 373 | unregister_framebuffer(&drvdata->info); |
375 | 374 | ||
376 | fb_dealloc_cmap(&drvdata->info.cmap); | 375 | fb_dealloc_cmap(&drvdata->info.cmap); |
377 | 376 | ||
378 | if (drvdata->fb_alloced) | 377 | if (drvdata->fb_alloced) |
379 | dma_free_coherent(dev, PAGE_ALIGN(drvdata->info.fix.smem_len), | 378 | dma_free_coherent(dev, PAGE_ALIGN(drvdata->info.fix.smem_len), |
380 | drvdata->fb_virt, drvdata->fb_phys); | 379 | drvdata->fb_virt, drvdata->fb_phys); |
381 | else | 380 | else |
382 | iounmap(drvdata->fb_virt); | 381 | iounmap(drvdata->fb_virt); |
383 | 382 | ||
384 | /* Turn off the display */ | 383 | /* Turn off the display */ |
385 | xilinx_fb_out_be32(drvdata, REG_CTRL, 0); | 384 | xilinx_fb_out_be32(drvdata, REG_CTRL, 0); |
386 | 385 | ||
387 | /* Release the resources, as allocated based on interface */ | 386 | /* Release the resources, as allocated based on interface */ |
388 | if (drvdata->flags & PLB_ACCESS_FLAG) { | 387 | if (drvdata->flags & PLB_ACCESS_FLAG) { |
389 | iounmap(drvdata->regs); | 388 | iounmap(drvdata->regs); |
390 | release_mem_region(drvdata->regs_phys, 8); | 389 | release_mem_region(drvdata->regs_phys, 8); |
391 | } | 390 | } |
392 | #ifdef CONFIG_PPC_DCR | 391 | #ifdef CONFIG_PPC_DCR |
393 | else | 392 | else |
394 | dcr_unmap(drvdata->dcr_host, drvdata->dcr_len); | 393 | dcr_unmap(drvdata->dcr_host, drvdata->dcr_len); |
395 | #endif | 394 | #endif |
396 | 395 | ||
397 | kfree(drvdata); | 396 | kfree(drvdata); |
398 | dev_set_drvdata(dev, NULL); | 397 | dev_set_drvdata(dev, NULL); |
399 | 398 | ||
400 | return 0; | 399 | return 0; |
401 | } | 400 | } |
402 | 401 | ||
403 | /* --------------------------------------------------------------------- | 402 | /* --------------------------------------------------------------------- |
404 | * OF bus binding | 403 | * OF bus binding |
405 | */ | 404 | */ |
406 | 405 | ||
407 | static int __devinit xilinxfb_of_probe(struct platform_device *op) | 406 | static int __devinit xilinxfb_of_probe(struct platform_device *op) |
408 | { | 407 | { |
409 | const u32 *prop; | 408 | const u32 *prop; |
410 | u32 *p; | 409 | u32 *p; |
411 | u32 tft_access; | 410 | u32 tft_access; |
412 | struct xilinxfb_platform_data pdata; | 411 | struct xilinxfb_platform_data pdata; |
413 | struct resource res; | 412 | struct resource res; |
414 | int size, rc; | 413 | int size, rc; |
415 | struct xilinxfb_drvdata *drvdata; | 414 | struct xilinxfb_drvdata *drvdata; |
416 | 415 | ||
417 | /* Copy with the default pdata (not a ptr reference!) */ | 416 | /* Copy with the default pdata (not a ptr reference!) */ |
418 | pdata = xilinx_fb_default_pdata; | 417 | pdata = xilinx_fb_default_pdata; |
419 | 418 | ||
420 | /* Allocate the driver data region */ | 419 | /* Allocate the driver data region */ |
421 | drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); | 420 | drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); |
422 | if (!drvdata) { | 421 | if (!drvdata) { |
423 | dev_err(&op->dev, "Couldn't allocate device private record\n"); | 422 | dev_err(&op->dev, "Couldn't allocate device private record\n"); |
424 | return -ENOMEM; | 423 | return -ENOMEM; |
425 | } | 424 | } |
426 | 425 | ||
427 | /* | 426 | /* |
428 | * To check whether the core is connected directly to DCR or PLB | 427 | * To check whether the core is connected directly to DCR or PLB |
429 | * interface and initialize the tft_access accordingly. | 428 | * interface and initialize the tft_access accordingly. |
430 | */ | 429 | */ |
431 | p = (u32 *)of_get_property(op->dev.of_node, "xlnx,dcr-splb-slave-if", NULL); | 430 | p = (u32 *)of_get_property(op->dev.of_node, "xlnx,dcr-splb-slave-if", NULL); |
432 | tft_access = p ? *p : 0; | 431 | tft_access = p ? *p : 0; |
433 | 432 | ||
434 | /* | 433 | /* |
435 | * Fill the resource structure if its direct PLB interface | 434 | * Fill the resource structure if its direct PLB interface |
436 | * otherwise fill the dcr_host structure. | 435 | * otherwise fill the dcr_host structure. |
437 | */ | 436 | */ |
438 | if (tft_access) { | 437 | if (tft_access) { |
439 | drvdata->flags |= PLB_ACCESS_FLAG; | 438 | drvdata->flags |= PLB_ACCESS_FLAG; |
440 | rc = of_address_to_resource(op->dev.of_node, 0, &res); | 439 | rc = of_address_to_resource(op->dev.of_node, 0, &res); |
441 | if (rc) { | 440 | if (rc) { |
442 | dev_err(&op->dev, "invalid address\n"); | 441 | dev_err(&op->dev, "invalid address\n"); |
443 | goto err; | 442 | goto err; |
444 | } | 443 | } |
445 | } | 444 | } |
446 | #ifdef CONFIG_PPC_DCR | 445 | #ifdef CONFIG_PPC_DCR |
447 | else { | 446 | else { |
448 | int start; | 447 | int start; |
449 | res.start = 0; | 448 | res.start = 0; |
450 | start = dcr_resource_start(op->dev.of_node, 0); | 449 | start = dcr_resource_start(op->dev.of_node, 0); |
451 | drvdata->dcr_len = dcr_resource_len(op->dev.of_node, 0); | 450 | drvdata->dcr_len = dcr_resource_len(op->dev.of_node, 0); |
452 | drvdata->dcr_host = dcr_map(op->dev.of_node, start, drvdata->dcr_len); | 451 | drvdata->dcr_host = dcr_map(op->dev.of_node, start, drvdata->dcr_len); |
453 | if (!DCR_MAP_OK(drvdata->dcr_host)) { | 452 | if (!DCR_MAP_OK(drvdata->dcr_host)) { |
454 | dev_err(&op->dev, "invalid DCR address\n"); | 453 | dev_err(&op->dev, "invalid DCR address\n"); |
455 | goto err; | 454 | goto err; |
456 | } | 455 | } |
457 | } | 456 | } |
458 | #endif | 457 | #endif |
459 | 458 | ||
460 | prop = of_get_property(op->dev.of_node, "phys-size", &size); | 459 | prop = of_get_property(op->dev.of_node, "phys-size", &size); |
461 | if ((prop) && (size >= sizeof(u32)*2)) { | 460 | if ((prop) && (size >= sizeof(u32)*2)) { |
462 | pdata.screen_width_mm = prop[0]; | 461 | pdata.screen_width_mm = prop[0]; |
463 | pdata.screen_height_mm = prop[1]; | 462 | pdata.screen_height_mm = prop[1]; |
464 | } | 463 | } |
465 | 464 | ||
466 | prop = of_get_property(op->dev.of_node, "resolution", &size); | 465 | prop = of_get_property(op->dev.of_node, "resolution", &size); |
467 | if ((prop) && (size >= sizeof(u32)*2)) { | 466 | if ((prop) && (size >= sizeof(u32)*2)) { |
468 | pdata.xres = prop[0]; | 467 | pdata.xres = prop[0]; |
469 | pdata.yres = prop[1]; | 468 | pdata.yres = prop[1]; |
470 | } | 469 | } |
471 | 470 | ||
472 | prop = of_get_property(op->dev.of_node, "virtual-resolution", &size); | 471 | prop = of_get_property(op->dev.of_node, "virtual-resolution", &size); |
473 | if ((prop) && (size >= sizeof(u32)*2)) { | 472 | if ((prop) && (size >= sizeof(u32)*2)) { |
474 | pdata.xvirt = prop[0]; | 473 | pdata.xvirt = prop[0]; |
475 | pdata.yvirt = prop[1]; | 474 | pdata.yvirt = prop[1]; |
476 | } | 475 | } |
477 | 476 | ||
478 | if (of_find_property(op->dev.of_node, "rotate-display", NULL)) | 477 | if (of_find_property(op->dev.of_node, "rotate-display", NULL)) |
479 | pdata.rotate_screen = 1; | 478 | pdata.rotate_screen = 1; |
480 | 479 | ||
481 | dev_set_drvdata(&op->dev, drvdata); | 480 | dev_set_drvdata(&op->dev, drvdata); |
482 | return xilinxfb_assign(&op->dev, drvdata, res.start, &pdata); | 481 | return xilinxfb_assign(&op->dev, drvdata, res.start, &pdata); |
483 | 482 | ||
484 | err: | 483 | err: |
485 | kfree(drvdata); | 484 | kfree(drvdata); |
486 | return -ENODEV; | 485 | return -ENODEV; |
487 | } | 486 | } |
488 | 487 | ||
489 | static int __devexit xilinxfb_of_remove(struct platform_device *op) | 488 | static int __devexit xilinxfb_of_remove(struct platform_device *op) |
490 | { | 489 | { |
491 | return xilinxfb_release(&op->dev); | 490 | return xilinxfb_release(&op->dev); |
492 | } | 491 | } |
493 | 492 | ||
494 | /* Match table for of_platform binding */ | 493 | /* Match table for of_platform binding */ |
495 | static struct of_device_id xilinxfb_of_match[] __devinitdata = { | 494 | static struct of_device_id xilinxfb_of_match[] __devinitdata = { |
496 | { .compatible = "xlnx,xps-tft-1.00.a", }, | 495 | { .compatible = "xlnx,xps-tft-1.00.a", }, |
497 | { .compatible = "xlnx,xps-tft-2.00.a", }, | 496 | { .compatible = "xlnx,xps-tft-2.00.a", }, |
498 | { .compatible = "xlnx,xps-tft-2.01.a", }, | 497 | { .compatible = "xlnx,xps-tft-2.01.a", }, |
499 | { .compatible = "xlnx,plb-tft-cntlr-ref-1.00.a", }, | 498 | { .compatible = "xlnx,plb-tft-cntlr-ref-1.00.a", }, |
500 | { .compatible = "xlnx,plb-dvi-cntlr-ref-1.00.c", }, | 499 | { .compatible = "xlnx,plb-dvi-cntlr-ref-1.00.c", }, |
501 | {}, | 500 | {}, |
502 | }; | 501 | }; |
503 | MODULE_DEVICE_TABLE(of, xilinxfb_of_match); | 502 | MODULE_DEVICE_TABLE(of, xilinxfb_of_match); |
504 | 503 | ||
505 | static struct platform_driver xilinxfb_of_driver = { | 504 | static struct platform_driver xilinxfb_of_driver = { |
506 | .probe = xilinxfb_of_probe, | 505 | .probe = xilinxfb_of_probe, |
507 | .remove = __devexit_p(xilinxfb_of_remove), | 506 | .remove = __devexit_p(xilinxfb_of_remove), |
508 | .driver = { | 507 | .driver = { |
509 | .name = DRIVER_NAME, | 508 | .name = DRIVER_NAME, |
510 | .owner = THIS_MODULE, | 509 | .owner = THIS_MODULE, |
511 | .of_match_table = xilinxfb_of_match, | 510 | .of_match_table = xilinxfb_of_match, |
512 | }, | 511 | }, |
513 | }; | 512 | }; |
514 | 513 | ||
515 | 514 | ||
516 | /* --------------------------------------------------------------------- | 515 | /* --------------------------------------------------------------------- |
517 | * Module setup and teardown | 516 | * Module setup and teardown |
518 | */ | 517 | */ |
519 | 518 | ||
520 | static int __init | 519 | static int __init |
521 | xilinxfb_init(void) | 520 | xilinxfb_init(void) |
522 | { | 521 | { |
523 | return platform_driver_register(&xilinxfb_of_driver); | 522 | return platform_driver_register(&xilinxfb_of_driver); |
524 | } | 523 | } |
525 | 524 | ||
526 | static void __exit | 525 | static void __exit |
527 | xilinxfb_cleanup(void) | 526 | xilinxfb_cleanup(void) |
528 | { | 527 | { |
529 | platform_driver_unregister(&xilinxfb_of_driver); | 528 | platform_driver_unregister(&xilinxfb_of_driver); |
530 | } | 529 | } |
531 | 530 | ||
532 | module_init(xilinxfb_init); | 531 | module_init(xilinxfb_init); |
533 | module_exit(xilinxfb_cleanup); | 532 | module_exit(xilinxfb_cleanup); |
534 | 533 | ||
535 | MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); | 534 | MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); |
536 | MODULE_DESCRIPTION("Xilinx TFT frame buffer driver"); | 535 | MODULE_DESCRIPTION("Xilinx TFT frame buffer driver"); |
537 | MODULE_LICENSE("GPL"); | 536 | MODULE_LICENSE("GPL"); |
538 | 537 |