diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/video/tegra/dc/nvsd.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/video/tegra/dc/nvsd.c')
-rw-r--r-- | drivers/video/tegra/dc/nvsd.c | 906 |
1 files changed, 906 insertions, 0 deletions
diff --git a/drivers/video/tegra/dc/nvsd.c b/drivers/video/tegra/dc/nvsd.c new file mode 100644 index 00000000000..a2f3ece6ae7 --- /dev/null +++ b/drivers/video/tegra/dc/nvsd.c | |||
@@ -0,0 +1,906 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/dc/nvsd.c | ||
3 | * | ||
4 | * Copyright (c) 2010-2012, NVIDIA Corporation. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <mach/dc.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/backlight.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | |||
25 | #include "dc_reg.h" | ||
26 | #include "dc_priv.h" | ||
27 | #include "nvsd.h" | ||
28 | |||
29 | /* Elements for sysfs access */ | ||
30 | #define NVSD_ATTR(__name) static struct kobj_attribute nvsd_attr_##__name = \ | ||
31 | __ATTR(__name, S_IRUGO|S_IWUSR, nvsd_settings_show, nvsd_settings_store) | ||
32 | #define NVSD_ATTRS_ENTRY(__name) (&nvsd_attr_##__name.attr) | ||
33 | #define IS_NVSD_ATTR(__name) (attr == &nvsd_attr_##__name) | ||
34 | |||
35 | static ssize_t nvsd_settings_show(struct kobject *kobj, | ||
36 | struct kobj_attribute *attr, char *buf); | ||
37 | |||
38 | static ssize_t nvsd_settings_store(struct kobject *kobj, | ||
39 | struct kobj_attribute *attr, const char *buf, size_t count); | ||
40 | |||
41 | static ssize_t nvsd_registers_show(struct kobject *kobj, | ||
42 | struct kobj_attribute *attr, char *buf); | ||
43 | |||
44 | NVSD_ATTR(enable); | ||
45 | NVSD_ATTR(aggressiveness); | ||
46 | NVSD_ATTR(phase_in_settings); | ||
47 | NVSD_ATTR(phase_in_adjustments); | ||
48 | NVSD_ATTR(bin_width); | ||
49 | NVSD_ATTR(hw_update_delay); | ||
50 | NVSD_ATTR(use_vid_luma); | ||
51 | NVSD_ATTR(coeff); | ||
52 | NVSD_ATTR(blp_time_constant); | ||
53 | NVSD_ATTR(blp_step); | ||
54 | NVSD_ATTR(fc_time_limit); | ||
55 | NVSD_ATTR(fc_threshold); | ||
56 | NVSD_ATTR(lut); | ||
57 | NVSD_ATTR(bltf); | ||
58 | static struct kobj_attribute nvsd_attr_registers = | ||
59 | __ATTR(registers, S_IRUGO, nvsd_registers_show, NULL); | ||
60 | |||
61 | static struct attribute *nvsd_attrs[] = { | ||
62 | NVSD_ATTRS_ENTRY(enable), | ||
63 | NVSD_ATTRS_ENTRY(aggressiveness), | ||
64 | NVSD_ATTRS_ENTRY(phase_in_settings), | ||
65 | NVSD_ATTRS_ENTRY(phase_in_adjustments), | ||
66 | NVSD_ATTRS_ENTRY(bin_width), | ||
67 | NVSD_ATTRS_ENTRY(hw_update_delay), | ||
68 | NVSD_ATTRS_ENTRY(use_vid_luma), | ||
69 | NVSD_ATTRS_ENTRY(coeff), | ||
70 | NVSD_ATTRS_ENTRY(blp_time_constant), | ||
71 | NVSD_ATTRS_ENTRY(blp_step), | ||
72 | NVSD_ATTRS_ENTRY(fc_time_limit), | ||
73 | NVSD_ATTRS_ENTRY(fc_threshold), | ||
74 | NVSD_ATTRS_ENTRY(lut), | ||
75 | NVSD_ATTRS_ENTRY(bltf), | ||
76 | NVSD_ATTRS_ENTRY(registers), | ||
77 | NULL, | ||
78 | }; | ||
79 | |||
80 | static struct attribute_group nvsd_attr_group = { | ||
81 | .attrs = nvsd_attrs, | ||
82 | }; | ||
83 | |||
84 | static struct kobject *nvsd_kobj; | ||
85 | |||
86 | /* shared brightness variable */ | ||
87 | static atomic_t *sd_brightness = NULL; | ||
88 | /* shared boolean for manual K workaround */ | ||
89 | static atomic_t man_k_until_blank = ATOMIC_INIT(0); | ||
90 | |||
91 | static u8 nvsd_get_bw_idx(struct tegra_dc_sd_settings *settings) | ||
92 | { | ||
93 | u8 bw; | ||
94 | |||
95 | switch (settings->bin_width) { | ||
96 | default: | ||
97 | case -1: | ||
98 | /* A -1 bin-width indicates 'automatic' | ||
99 | based upon aggressiveness. */ | ||
100 | settings->bin_width = -1; | ||
101 | switch (settings->aggressiveness) { | ||
102 | default: | ||
103 | case 0: | ||
104 | case 1: | ||
105 | bw = SD_BIN_WIDTH_ONE; | ||
106 | break; | ||
107 | case 2: | ||
108 | case 3: | ||
109 | case 4: | ||
110 | bw = SD_BIN_WIDTH_TWO; | ||
111 | break; | ||
112 | case 5: | ||
113 | bw = SD_BIN_WIDTH_FOUR; | ||
114 | break; | ||
115 | } | ||
116 | break; | ||
117 | case 1: | ||
118 | bw = SD_BIN_WIDTH_ONE; | ||
119 | break; | ||
120 | case 2: | ||
121 | bw = SD_BIN_WIDTH_TWO; | ||
122 | break; | ||
123 | case 4: | ||
124 | bw = SD_BIN_WIDTH_FOUR; | ||
125 | break; | ||
126 | case 8: | ||
127 | bw = SD_BIN_WIDTH_EIGHT; | ||
128 | break; | ||
129 | } | ||
130 | return bw >> 3; | ||
131 | |||
132 | } | ||
133 | |||
134 | static bool nvsd_phase_in_adjustments(struct tegra_dc *dc, | ||
135 | struct tegra_dc_sd_settings *settings) | ||
136 | { | ||
137 | u8 step, cur_sd_brightness; | ||
138 | u16 target_k, cur_k; | ||
139 | u32 man_k, val; | ||
140 | |||
141 | cur_sd_brightness = atomic_read(sd_brightness); | ||
142 | |||
143 | target_k = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES); | ||
144 | target_k = SD_HW_K_R(target_k); | ||
145 | cur_k = tegra_dc_readl(dc, DC_DISP_SD_MAN_K_VALUES); | ||
146 | cur_k = SD_HW_K_R(cur_k); | ||
147 | |||
148 | /* read brightness value */ | ||
149 | val = tegra_dc_readl(dc, DC_DISP_SD_BL_CONTROL); | ||
150 | val = SD_BLC_BRIGHTNESS(val); | ||
151 | |||
152 | step = settings->phase_adj_step; | ||
153 | if (cur_sd_brightness != val || target_k != cur_k) { | ||
154 | if (!step) | ||
155 | step = ADJ_PHASE_STEP; | ||
156 | |||
157 | /* Phase in Backlight and Pixel K | ||
158 | every ADJ_PHASE_STEP frames*/ | ||
159 | if ((step-- & ADJ_PHASE_STEP) == ADJ_PHASE_STEP) { | ||
160 | |||
161 | if (val != cur_sd_brightness) | ||
162 | val > cur_sd_brightness ? | ||
163 | (cur_sd_brightness++) : | ||
164 | (cur_sd_brightness--); | ||
165 | |||
166 | if (target_k != cur_k) { | ||
167 | if (target_k > cur_k) | ||
168 | cur_k += K_STEP; | ||
169 | else | ||
170 | cur_k -= K_STEP; | ||
171 | } | ||
172 | |||
173 | /* Set manual k value */ | ||
174 | man_k = SD_MAN_K_R(cur_k) | | ||
175 | SD_MAN_K_G(cur_k) | SD_MAN_K_B(cur_k); | ||
176 | tegra_dc_writel(dc, man_k, DC_DISP_SD_MAN_K_VALUES); | ||
177 | /* Set manual brightness value */ | ||
178 | atomic_set(sd_brightness, cur_sd_brightness); | ||
179 | } | ||
180 | settings->phase_adj_step = step; | ||
181 | return true; | ||
182 | } else | ||
183 | return false; | ||
184 | } | ||
185 | |||
186 | /* phase in the luts based on the current and max step */ | ||
187 | static void nvsd_phase_in_luts(struct tegra_dc_sd_settings *settings, | ||
188 | struct tegra_dc *dc) | ||
189 | { | ||
190 | u32 val; | ||
191 | u8 bw_idx; | ||
192 | int i; | ||
193 | u16 phase_settings_step = settings->phase_settings_step; | ||
194 | u16 num_phase_in_steps = settings->num_phase_in_steps; | ||
195 | |||
196 | bw_idx = nvsd_get_bw_idx(settings); | ||
197 | |||
198 | /* Phase in Final LUT */ | ||
199 | for (i = 0; i < DC_DISP_SD_LUT_NUM; i++) { | ||
200 | val = SD_LUT_R((settings->lut[bw_idx][i].r * | ||
201 | phase_settings_step)/num_phase_in_steps) | | ||
202 | SD_LUT_G((settings->lut[bw_idx][i].g * | ||
203 | phase_settings_step)/num_phase_in_steps) | | ||
204 | SD_LUT_B((settings->lut[bw_idx][i].b * | ||
205 | phase_settings_step)/num_phase_in_steps); | ||
206 | |||
207 | tegra_dc_writel(dc, val, DC_DISP_SD_LUT(i)); | ||
208 | } | ||
209 | /* Phase in Final BLTF */ | ||
210 | for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) { | ||
211 | val = SD_BL_TF_POINT_0(255-((255-settings->bltf[bw_idx][i][0]) | ||
212 | * phase_settings_step)/num_phase_in_steps) | | ||
213 | SD_BL_TF_POINT_1(255-((255-settings->bltf[bw_idx][i][1]) | ||
214 | * phase_settings_step)/num_phase_in_steps) | | ||
215 | SD_BL_TF_POINT_2(255-((255-settings->bltf[bw_idx][i][2]) | ||
216 | * phase_settings_step)/num_phase_in_steps) | | ||
217 | SD_BL_TF_POINT_3(255-((255-settings->bltf[bw_idx][i][3]) | ||
218 | * phase_settings_step)/num_phase_in_steps); | ||
219 | |||
220 | tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i)); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | /* handle the commands that may be invoked for phase_in_settings */ | ||
225 | static void nvsd_cmd_handler(struct tegra_dc_sd_settings *settings, | ||
226 | struct tegra_dc *dc) | ||
227 | { | ||
228 | u32 val; | ||
229 | u8 bw_idx, bw; | ||
230 | |||
231 | if (settings->cmd & ENABLE) { | ||
232 | settings->phase_settings_step++; | ||
233 | if (settings->phase_settings_step >= | ||
234 | settings->num_phase_in_steps) | ||
235 | settings->cmd &= ~ENABLE; | ||
236 | |||
237 | nvsd_phase_in_luts(settings, dc); | ||
238 | } | ||
239 | if (settings->cmd & DISABLE) { | ||
240 | settings->phase_settings_step--; | ||
241 | nvsd_phase_in_luts(settings, dc); | ||
242 | if (settings->phase_settings_step == 0) { | ||
243 | /* finish up aggressiveness phase in */ | ||
244 | if (settings->cmd & AGG_CHG) | ||
245 | settings->aggressiveness = settings->final_agg; | ||
246 | settings->cmd = NO_CMD; | ||
247 | settings->enable = 0; | ||
248 | nvsd_init(dc, settings); | ||
249 | } | ||
250 | } | ||
251 | if (settings->cmd & AGG_CHG) { | ||
252 | if (settings->aggressiveness == settings->final_agg) | ||
253 | settings->cmd &= ~AGG_CHG; | ||
254 | if ((settings->cur_agg_step++ & (STEPS_PER_AGG_CHG - 1)) == 0) { | ||
255 | settings->final_agg > settings->aggressiveness ? | ||
256 | settings->aggressiveness++ : | ||
257 | settings->aggressiveness--; | ||
258 | |||
259 | /* Update aggressiveness value in HW */ | ||
260 | val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL); | ||
261 | val &= ~SD_AGGRESSIVENESS(0x7); | ||
262 | val |= SD_AGGRESSIVENESS(settings->aggressiveness); | ||
263 | |||
264 | /* Adjust bin_width for automatic setting */ | ||
265 | if (settings->bin_width == -1) { | ||
266 | bw_idx = nvsd_get_bw_idx(settings); | ||
267 | |||
268 | bw = bw_idx << 3; | ||
269 | |||
270 | val &= ~SD_BIN_WIDTH_MASK; | ||
271 | val |= bw; | ||
272 | } | ||
273 | tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL); | ||
274 | |||
275 | nvsd_phase_in_luts(settings, dc); | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | |||
280 | static bool nvsd_update_enable(struct tegra_dc_sd_settings *settings, | ||
281 | int enable_val) | ||
282 | { | ||
283 | |||
284 | if (enable_val != 1 && enable_val != 0) | ||
285 | return false; | ||
286 | |||
287 | if (!settings->cmd && settings->enable != enable_val) { | ||
288 | settings->num_phase_in_steps = | ||
289 | STEPS_PER_AGG_LVL*settings->aggressiveness; | ||
290 | settings->phase_settings_step = enable_val ? | ||
291 | 0 : settings->num_phase_in_steps; | ||
292 | } | ||
293 | |||
294 | if (settings->enable != enable_val || settings->cmd & DISABLE) { | ||
295 | settings->cmd &= ~(ENABLE | DISABLE); | ||
296 | if (!settings->enable && enable_val) | ||
297 | settings->cmd |= PHASE_IN; | ||
298 | settings->cmd |= enable_val ? ENABLE : DISABLE; | ||
299 | return true; | ||
300 | } | ||
301 | |||
302 | return false; | ||
303 | } | ||
304 | |||
305 | static bool nvsd_update_agg(struct tegra_dc_sd_settings *settings, int agg_val) | ||
306 | { | ||
307 | int i; | ||
308 | int pri_lvl = SD_AGG_PRI_LVL(agg_val); | ||
309 | int agg_lvl = SD_GET_AGG(agg_val); | ||
310 | struct tegra_dc_sd_agg_priorities *sd_agg_priorities = | ||
311 | &settings->agg_priorities; | ||
312 | |||
313 | if (agg_lvl > 5 || agg_lvl < 0) | ||
314 | return false; | ||
315 | else if (agg_lvl == 0 && pri_lvl == 0) | ||
316 | return false; | ||
317 | |||
318 | if (pri_lvl >= 0 && pri_lvl < 4) | ||
319 | sd_agg_priorities->agg[pri_lvl] = agg_lvl; | ||
320 | |||
321 | for (i = NUM_AGG_PRI_LVLS - 1; i >= 0; i--) { | ||
322 | if (sd_agg_priorities->agg[i]) | ||
323 | break; | ||
324 | } | ||
325 | |||
326 | sd_agg_priorities->pri_lvl = i; | ||
327 | pri_lvl = i; | ||
328 | agg_lvl = sd_agg_priorities->agg[i]; | ||
329 | |||
330 | if (settings->phase_in_settings && settings->enable && | ||
331 | settings->aggressiveness != agg_lvl) { | ||
332 | |||
333 | settings->final_agg = agg_lvl; | ||
334 | settings->cmd |= AGG_CHG; | ||
335 | settings->cur_agg_step = 0; | ||
336 | return true; | ||
337 | } else if (settings->aggressiveness != agg_lvl) { | ||
338 | settings->aggressiveness = agg_lvl; | ||
339 | return true; | ||
340 | } | ||
341 | |||
342 | return false; | ||
343 | } | ||
344 | |||
345 | /* Functional initialization */ | ||
346 | void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) | ||
347 | { | ||
348 | u32 i = 0; | ||
349 | u32 val = 0; | ||
350 | u32 bw_idx = 0; | ||
351 | /* TODO: check if HW says SD's available */ | ||
352 | |||
353 | /* If SD's not present or disabled, clear the register and return. */ | ||
354 | if (!settings || settings->enable == 0) { | ||
355 | /* clear the brightness val, too. */ | ||
356 | if (sd_brightness) | ||
357 | atomic_set(sd_brightness, 255); | ||
358 | |||
359 | sd_brightness = NULL; | ||
360 | |||
361 | if (settings) | ||
362 | settings->phase_settings_step = 0; | ||
363 | tegra_dc_writel(dc, 0, DC_DISP_SD_CONTROL); | ||
364 | return; | ||
365 | } | ||
366 | |||
367 | dev_dbg(&dc->ndev->dev, "NVSD Init:\n"); | ||
368 | |||
369 | /* init agg_priorities */ | ||
370 | if (!settings->agg_priorities.agg[0]) | ||
371 | settings->agg_priorities.agg[0] = settings->aggressiveness; | ||
372 | |||
373 | /* WAR: Settings will not be valid until the next flip. | ||
374 | * Thus, set manual K to either HW's current value (if | ||
375 | * we're already enabled) or a non-effective value (if | ||
376 | * we're about to enable). */ | ||
377 | val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL); | ||
378 | |||
379 | if (val & SD_ENABLE_NORMAL) | ||
380 | i = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES); | ||
381 | else | ||
382 | i = 0; /* 0 values for RGB = 1.0, i.e. non-affected */ | ||
383 | |||
384 | tegra_dc_writel(dc, i, DC_DISP_SD_MAN_K_VALUES); | ||
385 | /* Enable manual correction mode here so that changing the | ||
386 | * settings won't immediately impact display dehavior. */ | ||
387 | val |= SD_CORRECTION_MODE_MAN; | ||
388 | tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL); | ||
389 | |||
390 | bw_idx = nvsd_get_bw_idx(settings); | ||
391 | |||
392 | /* Write LUT */ | ||
393 | if (!settings->cmd) { | ||
394 | dev_dbg(&dc->ndev->dev, " LUT:\n"); | ||
395 | |||
396 | for (i = 0; i < DC_DISP_SD_LUT_NUM; i++) { | ||
397 | val = SD_LUT_R(settings->lut[bw_idx][i].r) | | ||
398 | SD_LUT_G(settings->lut[bw_idx][i].g) | | ||
399 | SD_LUT_B(settings->lut[bw_idx][i].b); | ||
400 | tegra_dc_writel(dc, val, DC_DISP_SD_LUT(i)); | ||
401 | |||
402 | dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val); | ||
403 | } | ||
404 | } | ||
405 | |||
406 | /* Write BL TF */ | ||
407 | if (!settings->cmd) { | ||
408 | dev_dbg(&dc->ndev->dev, " BL_TF:\n"); | ||
409 | |||
410 | for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) { | ||
411 | val = SD_BL_TF_POINT_0(settings->bltf[bw_idx][i][0]) | | ||
412 | SD_BL_TF_POINT_1(settings->bltf[bw_idx][i][1]) | | ||
413 | SD_BL_TF_POINT_2(settings->bltf[bw_idx][i][2]) | | ||
414 | SD_BL_TF_POINT_3(settings->bltf[bw_idx][i][3]); | ||
415 | |||
416 | tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i)); | ||
417 | |||
418 | dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val); | ||
419 | } | ||
420 | } else if ((settings->cmd & PHASE_IN)) { | ||
421 | settings->cmd &= ~PHASE_IN; | ||
422 | /* Write NO_OP values for BLTF */ | ||
423 | for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) { | ||
424 | val = SD_BL_TF_POINT_0(0xFF) | | ||
425 | SD_BL_TF_POINT_1(0xFF) | | ||
426 | SD_BL_TF_POINT_2(0xFF) | | ||
427 | SD_BL_TF_POINT_3(0xFF); | ||
428 | |||
429 | tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i)); | ||
430 | |||
431 | dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val); | ||
432 | } | ||
433 | } | ||
434 | |||
435 | /* Set step correctly on init */ | ||
436 | if (!settings->cmd && settings->phase_in_settings) { | ||
437 | settings->num_phase_in_steps = STEPS_PER_AGG_LVL * | ||
438 | settings->aggressiveness; | ||
439 | settings->phase_settings_step = settings->enable ? | ||
440 | settings->num_phase_in_steps : 0; | ||
441 | } | ||
442 | |||
443 | /* Write Coeff */ | ||
444 | val = SD_CSC_COEFF_R(settings->coeff.r) | | ||
445 | SD_CSC_COEFF_G(settings->coeff.g) | | ||
446 | SD_CSC_COEFF_B(settings->coeff.b); | ||
447 | tegra_dc_writel(dc, val, DC_DISP_SD_CSC_COEFF); | ||
448 | dev_dbg(&dc->ndev->dev, " COEFF: 0x%08x\n", val); | ||
449 | |||
450 | /* Write BL Params */ | ||
451 | val = SD_BLP_TIME_CONSTANT(settings->blp.time_constant) | | ||
452 | SD_BLP_STEP(settings->blp.step); | ||
453 | tegra_dc_writel(dc, val, DC_DISP_SD_BL_PARAMETERS); | ||
454 | dev_dbg(&dc->ndev->dev, " BLP: 0x%08x\n", val); | ||
455 | |||
456 | /* Write Auto/Manual PWM */ | ||
457 | val = (settings->use_auto_pwm) ? SD_BLC_MODE_AUTO : SD_BLC_MODE_MAN; | ||
458 | tegra_dc_writel(dc, val, DC_DISP_SD_BL_CONTROL); | ||
459 | dev_dbg(&dc->ndev->dev, " BL_CONTROL: 0x%08x\n", val); | ||
460 | |||
461 | /* Write Flicker Control */ | ||
462 | val = SD_FC_TIME_LIMIT(settings->fc.time_limit) | | ||
463 | SD_FC_THRESHOLD(settings->fc.threshold); | ||
464 | tegra_dc_writel(dc, val, DC_DISP_SD_FLICKER_CONTROL); | ||
465 | dev_dbg(&dc->ndev->dev, " FLICKER_CONTROL: 0x%08x\n", val); | ||
466 | |||
467 | /* Manage SD Control */ | ||
468 | val = 0; | ||
469 | /* Stay in manual correction mode until the next flip. */ | ||
470 | val |= SD_CORRECTION_MODE_MAN; | ||
471 | /* Enable / One-Shot */ | ||
472 | val |= (settings->enable == 2) ? | ||
473 | (SD_ENABLE_ONESHOT | SD_ONESHOT_ENABLE) : | ||
474 | SD_ENABLE_NORMAL; | ||
475 | /* HW Update Delay */ | ||
476 | val |= SD_HW_UPDATE_DLY(settings->hw_update_delay); | ||
477 | /* Video Luma */ | ||
478 | val |= (settings->use_vid_luma) ? SD_USE_VID_LUMA : 0; | ||
479 | /* Aggressiveness */ | ||
480 | val |= SD_AGGRESSIVENESS(settings->aggressiveness); | ||
481 | /* Bin Width (value derived from bw_idx) */ | ||
482 | val |= bw_idx << 3; | ||
483 | /* Finally, Write SD Control */ | ||
484 | tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL); | ||
485 | dev_dbg(&dc->ndev->dev, " SD_CONTROL: 0x%08x\n", val); | ||
486 | |||
487 | /* set the brightness pointer */ | ||
488 | sd_brightness = settings->sd_brightness; | ||
489 | |||
490 | /* note that we're in manual K until the next flip */ | ||
491 | atomic_set(&man_k_until_blank, 1); | ||
492 | } | ||
493 | |||
494 | /* Periodic update */ | ||
495 | bool nvsd_update_brightness(struct tegra_dc *dc) | ||
496 | { | ||
497 | u32 val = 0; | ||
498 | int cur_sd_brightness; | ||
499 | struct tegra_dc_sd_settings *settings = dc->out->sd_settings; | ||
500 | |||
501 | if (sd_brightness) { | ||
502 | if (atomic_read(&man_k_until_blank) && | ||
503 | !settings->phase_in_adjustments) { | ||
504 | val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL); | ||
505 | val &= ~SD_CORRECTION_MODE_MAN; | ||
506 | tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL); | ||
507 | atomic_set(&man_k_until_blank, 0); | ||
508 | } | ||
509 | |||
510 | if (settings->cmd) | ||
511 | nvsd_cmd_handler(settings, dc); | ||
512 | |||
513 | /* nvsd_cmd_handler may turn off didim */ | ||
514 | if (!settings->enable) | ||
515 | return true; | ||
516 | |||
517 | cur_sd_brightness = atomic_read(sd_brightness); | ||
518 | |||
519 | /* read brightness value */ | ||
520 | val = tegra_dc_readl(dc, DC_DISP_SD_BL_CONTROL); | ||
521 | val = SD_BLC_BRIGHTNESS(val); | ||
522 | |||
523 | if (settings->phase_in_adjustments) { | ||
524 | return nvsd_phase_in_adjustments(dc, settings); | ||
525 | } else if (val != (u32)cur_sd_brightness) { | ||
526 | /* set brightness value and note the update */ | ||
527 | atomic_set(sd_brightness, (int)val); | ||
528 | return true; | ||
529 | } | ||
530 | } | ||
531 | |||
532 | /* No update needed. */ | ||
533 | return false; | ||
534 | } | ||
535 | |||
536 | static ssize_t nvsd_lut_show(struct tegra_dc_sd_settings *sd_settings, | ||
537 | char *buf, ssize_t res) | ||
538 | { | ||
539 | u32 i; | ||
540 | u32 j; | ||
541 | |||
542 | for (i = 0; i < NUM_BIN_WIDTHS; i++) { | ||
543 | res += snprintf(buf + res, PAGE_SIZE - res, | ||
544 | "Bin Width: %d\n", 1 << i); | ||
545 | |||
546 | for (j = 0; j < DC_DISP_SD_LUT_NUM; j++) { | ||
547 | res += snprintf(buf + res, | ||
548 | PAGE_SIZE - res, | ||
549 | "%d: R: %3d / G: %3d / B: %3d\n", | ||
550 | j, | ||
551 | sd_settings->lut[i][j].r, | ||
552 | sd_settings->lut[i][j].g, | ||
553 | sd_settings->lut[i][j].b); | ||
554 | } | ||
555 | } | ||
556 | return res; | ||
557 | } | ||
558 | |||
559 | static ssize_t nvsd_bltf_show(struct tegra_dc_sd_settings *sd_settings, | ||
560 | char *buf, ssize_t res) | ||
561 | { | ||
562 | u32 i; | ||
563 | u32 j; | ||
564 | |||
565 | for (i = 0; i < NUM_BIN_WIDTHS; i++) { | ||
566 | res += snprintf(buf + res, PAGE_SIZE - res, | ||
567 | "Bin Width: %d\n", 1 << i); | ||
568 | |||
569 | for (j = 0; j < DC_DISP_SD_BL_TF_NUM; j++) { | ||
570 | res += snprintf(buf + res, | ||
571 | PAGE_SIZE - res, | ||
572 | "%d: 0: %3d / 1: %3d / 2: %3d / 3: %3d\n", | ||
573 | j, | ||
574 | sd_settings->bltf[i][j][0], | ||
575 | sd_settings->bltf[i][j][1], | ||
576 | sd_settings->bltf[i][j][2], | ||
577 | sd_settings->bltf[i][j][3]); | ||
578 | } | ||
579 | } | ||
580 | return res; | ||
581 | } | ||
582 | |||
583 | /* Sysfs accessors */ | ||
584 | static ssize_t nvsd_settings_show(struct kobject *kobj, | ||
585 | struct kobj_attribute *attr, char *buf) | ||
586 | { | ||
587 | struct device *dev = container_of((kobj->parent), struct device, kobj); | ||
588 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
589 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
590 | struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings; | ||
591 | ssize_t res = 0; | ||
592 | |||
593 | if (sd_settings) { | ||
594 | if (IS_NVSD_ATTR(enable)) | ||
595 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
596 | sd_settings->enable); | ||
597 | else if (IS_NVSD_ATTR(aggressiveness)) | ||
598 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
599 | sd_settings->aggressiveness); | ||
600 | else if (IS_NVSD_ATTR(phase_in_settings)) | ||
601 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
602 | sd_settings->phase_in_settings); | ||
603 | else if (IS_NVSD_ATTR(phase_in_adjustments)) | ||
604 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
605 | sd_settings->phase_in_adjustments); | ||
606 | else if (IS_NVSD_ATTR(bin_width)) | ||
607 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
608 | sd_settings->bin_width); | ||
609 | else if (IS_NVSD_ATTR(hw_update_delay)) | ||
610 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
611 | sd_settings->hw_update_delay); | ||
612 | else if (IS_NVSD_ATTR(use_vid_luma)) | ||
613 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
614 | sd_settings->use_vid_luma); | ||
615 | else if (IS_NVSD_ATTR(coeff)) | ||
616 | res = snprintf(buf, PAGE_SIZE, | ||
617 | "R: %d / G: %d / B: %d\n", | ||
618 | sd_settings->coeff.r, | ||
619 | sd_settings->coeff.g, | ||
620 | sd_settings->coeff.b); | ||
621 | else if (IS_NVSD_ATTR(blp_time_constant)) | ||
622 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
623 | sd_settings->blp.time_constant); | ||
624 | else if (IS_NVSD_ATTR(blp_step)) | ||
625 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
626 | sd_settings->blp.step); | ||
627 | else if (IS_NVSD_ATTR(fc_time_limit)) | ||
628 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
629 | sd_settings->fc.time_limit); | ||
630 | else if (IS_NVSD_ATTR(fc_threshold)) | ||
631 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
632 | sd_settings->fc.threshold); | ||
633 | else if (IS_NVSD_ATTR(lut)) | ||
634 | res = nvsd_lut_show(sd_settings, buf, res); | ||
635 | else if (IS_NVSD_ATTR(bltf)) | ||
636 | res = nvsd_bltf_show(sd_settings, buf, res); | ||
637 | else | ||
638 | res = -EINVAL; | ||
639 | } else { | ||
640 | /* This shouldn't be reachable. But just in case... */ | ||
641 | res = -EINVAL; | ||
642 | } | ||
643 | |||
644 | return res; | ||
645 | } | ||
646 | |||
647 | #define nvsd_check_and_update(_min, _max, _varname) { \ | ||
648 | int val = simple_strtol(buf, NULL, 10); \ | ||
649 | if (val >= _min && val <= _max) { \ | ||
650 | sd_settings->_varname = val; \ | ||
651 | settings_updated = true; \ | ||
652 | } } | ||
653 | |||
654 | #define nvsd_get_multi(_ele, _num, _act, _min, _max) { \ | ||
655 | char *b, *c, *orig_b; \ | ||
656 | b = orig_b = kstrdup(buf, GFP_KERNEL); \ | ||
657 | for (_act = 0; _act < _num; _act++) { \ | ||
658 | if (!b) \ | ||
659 | break; \ | ||
660 | b = strim(b); \ | ||
661 | c = strsep(&b, " "); \ | ||
662 | if (!strlen(c)) \ | ||
663 | break; \ | ||
664 | _ele[_act] = simple_strtol(c, NULL, 10); \ | ||
665 | if (_ele[_act] < _min || _ele[_act] > _max) \ | ||
666 | break; \ | ||
667 | } \ | ||
668 | if (orig_b) \ | ||
669 | kfree(orig_b); \ | ||
670 | } | ||
671 | |||
672 | static int nvsd_lut_store(struct tegra_dc_sd_settings *sd_settings, | ||
673 | const char *buf) | ||
674 | { | ||
675 | int ele[3 * DC_DISP_SD_LUT_NUM * NUM_BIN_WIDTHS]; | ||
676 | int i = 0; | ||
677 | int j = 0; | ||
678 | int num = 3 * DC_DISP_SD_LUT_NUM * NUM_BIN_WIDTHS; | ||
679 | |||
680 | nvsd_get_multi(ele, num, i, 0, 255); | ||
681 | |||
682 | if (i != num) | ||
683 | return -EINVAL; | ||
684 | |||
685 | for (i = 0; i < NUM_BIN_WIDTHS; i++) { | ||
686 | for (j = 0; j < DC_DISP_SD_LUT_NUM; j++) { | ||
687 | sd_settings->lut[i][j].r = | ||
688 | ele[i * NUM_BIN_WIDTHS + j * 3 + 0]; | ||
689 | sd_settings->lut[i][j].g = | ||
690 | ele[i * NUM_BIN_WIDTHS + j * 3 + 1]; | ||
691 | sd_settings->lut[i][j].b = | ||
692 | ele[i * NUM_BIN_WIDTHS + j * 3 + 2]; | ||
693 | } | ||
694 | } | ||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | static int nvsd_bltf_store(struct tegra_dc_sd_settings *sd_settings, | ||
699 | const char *buf) | ||
700 | { | ||
701 | int ele[4 * DC_DISP_SD_BL_TF_NUM * NUM_BIN_WIDTHS]; | ||
702 | int i = 0, j = 0, num = ARRAY_SIZE(ele); | ||
703 | |||
704 | nvsd_get_multi(ele, num, i, 0, 255); | ||
705 | |||
706 | if (i != num) | ||
707 | return -EINVAL; | ||
708 | |||
709 | for (i = 0; i < NUM_BIN_WIDTHS; i++) { | ||
710 | for (j = 0; j < DC_DISP_SD_BL_TF_NUM; j++) { | ||
711 | size_t base = (i * NUM_BIN_WIDTHS * | ||
712 | DC_DISP_SD_BL_TF_NUM) + (j * 4); | ||
713 | sd_settings->bltf[i][j][0] = ele[base + 0]; | ||
714 | sd_settings->bltf[i][j][1] = ele[base + 1]; | ||
715 | sd_settings->bltf[i][j][2] = ele[base + 2]; | ||
716 | sd_settings->bltf[i][j][3] = ele[base + 3]; | ||
717 | } | ||
718 | } | ||
719 | |||
720 | return 0; | ||
721 | } | ||
722 | |||
723 | static ssize_t nvsd_settings_store(struct kobject *kobj, | ||
724 | struct kobj_attribute *attr, const char *buf, size_t count) | ||
725 | { | ||
726 | struct device *dev = container_of((kobj->parent), struct device, kobj); | ||
727 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
728 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
729 | struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings; | ||
730 | ssize_t res = count; | ||
731 | bool settings_updated = false; | ||
732 | long int result; | ||
733 | int err; | ||
734 | |||
735 | if (sd_settings) { | ||
736 | if (IS_NVSD_ATTR(enable)) { | ||
737 | if (sd_settings->phase_in_settings) { | ||
738 | err = strict_strtol(buf, 10, &result); | ||
739 | if (err) | ||
740 | return err; | ||
741 | |||
742 | if (nvsd_update_enable(sd_settings, result)) | ||
743 | nvsd_check_and_update(1, 1, enable); | ||
744 | |||
745 | } else { | ||
746 | nvsd_check_and_update(0, 1, enable); | ||
747 | } | ||
748 | } else if (IS_NVSD_ATTR(aggressiveness)) { | ||
749 | err = strict_strtol(buf, 10, &result); | ||
750 | if (err) | ||
751 | return err; | ||
752 | |||
753 | if (nvsd_update_agg(sd_settings, result) | ||
754 | && !sd_settings->phase_in_settings) | ||
755 | settings_updated = true; | ||
756 | |||
757 | } else if (IS_NVSD_ATTR(phase_in_settings)) { | ||
758 | nvsd_check_and_update(0, 1, phase_in_settings); | ||
759 | } else if (IS_NVSD_ATTR(phase_in_adjustments)) { | ||
760 | nvsd_check_and_update(0, 1, phase_in_adjustments); | ||
761 | } else if (IS_NVSD_ATTR(bin_width)) { | ||
762 | nvsd_check_and_update(0, 8, bin_width); | ||
763 | } else if (IS_NVSD_ATTR(hw_update_delay)) { | ||
764 | nvsd_check_and_update(0, 2, hw_update_delay); | ||
765 | } else if (IS_NVSD_ATTR(use_vid_luma)) { | ||
766 | nvsd_check_and_update(0, 1, use_vid_luma); | ||
767 | } else if (IS_NVSD_ATTR(coeff)) { | ||
768 | int ele[3], i = 0, num = 3; | ||
769 | nvsd_get_multi(ele, num, i, 0, 15); | ||
770 | |||
771 | if (i == num) { | ||
772 | sd_settings->coeff.r = ele[0]; | ||
773 | sd_settings->coeff.g = ele[1]; | ||
774 | sd_settings->coeff.b = ele[2]; | ||
775 | settings_updated = true; | ||
776 | } else { | ||
777 | res = -EINVAL; | ||
778 | } | ||
779 | } else if (IS_NVSD_ATTR(blp_time_constant)) { | ||
780 | nvsd_check_and_update(0, 1024, blp.time_constant); | ||
781 | } else if (IS_NVSD_ATTR(blp_step)) { | ||
782 | nvsd_check_and_update(0, 255, blp.step); | ||
783 | } else if (IS_NVSD_ATTR(fc_time_limit)) { | ||
784 | nvsd_check_and_update(0, 255, fc.time_limit); | ||
785 | } else if (IS_NVSD_ATTR(fc_threshold)) { | ||
786 | nvsd_check_and_update(0, 255, fc.threshold); | ||
787 | } else if (IS_NVSD_ATTR(lut)) { | ||
788 | if (nvsd_lut_store(sd_settings, buf)) | ||
789 | res = -EINVAL; | ||
790 | else | ||
791 | settings_updated = true; | ||
792 | } else if (IS_NVSD_ATTR(bltf)) { | ||
793 | if (nvsd_bltf_store(sd_settings, buf)) | ||
794 | res = -EINVAL; | ||
795 | else | ||
796 | settings_updated = true; | ||
797 | } else { | ||
798 | res = -EINVAL; | ||
799 | } | ||
800 | |||
801 | /* Re-init if our settings were updated. */ | ||
802 | if (settings_updated) { | ||
803 | mutex_lock(&dc->lock); | ||
804 | if (!dc->enabled) { | ||
805 | mutex_unlock(&dc->lock); | ||
806 | return -ENODEV; | ||
807 | } | ||
808 | mutex_unlock(&dc->lock); | ||
809 | |||
810 | nvsd_init(dc, sd_settings); | ||
811 | |||
812 | /* Update backlight state IFF we're disabling! */ | ||
813 | if (!sd_settings->enable && sd_settings->bl_device) { | ||
814 | /* Do the actual brightness update outside of | ||
815 | * the mutex */ | ||
816 | struct platform_device *pdev = | ||
817 | sd_settings->bl_device; | ||
818 | struct backlight_device *bl = | ||
819 | platform_get_drvdata(pdev); | ||
820 | |||
821 | if (bl) | ||
822 | backlight_update_status(bl); | ||
823 | } | ||
824 | } | ||
825 | } else { | ||
826 | /* This shouldn't be reachable. But just in case... */ | ||
827 | res = -EINVAL; | ||
828 | } | ||
829 | |||
830 | return res; | ||
831 | } | ||
832 | |||
833 | #define NVSD_PRINT_REG(__name) { \ | ||
834 | u32 val = tegra_dc_readl(dc, __name); \ | ||
835 | res += snprintf(buf + res, PAGE_SIZE - res, #__name ": 0x%08x\n", \ | ||
836 | val); \ | ||
837 | } | ||
838 | |||
839 | #define NVSD_PRINT_REG_ARRAY(__name) { \ | ||
840 | u32 val = 0, i = 0; \ | ||
841 | res += snprintf(buf + res, PAGE_SIZE - res, #__name ":\n"); \ | ||
842 | for (i = 0; i < __name##_NUM; i++) { \ | ||
843 | val = tegra_dc_readl(dc, __name(i)); \ | ||
844 | res += snprintf(buf + res, PAGE_SIZE - res, " %d: 0x%08x\n", \ | ||
845 | i, val); \ | ||
846 | } \ | ||
847 | } | ||
848 | |||
849 | static ssize_t nvsd_registers_show(struct kobject *kobj, | ||
850 | struct kobj_attribute *attr, char *buf) | ||
851 | { | ||
852 | struct device *dev = container_of((kobj->parent), struct device, kobj); | ||
853 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
854 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
855 | ssize_t res = 0; | ||
856 | |||
857 | mutex_lock(&dc->lock); | ||
858 | if (!dc->enabled) { | ||
859 | mutex_unlock(&dc->lock); | ||
860 | return -ENODEV; | ||
861 | } | ||
862 | |||
863 | mutex_unlock(&dc->lock); | ||
864 | NVSD_PRINT_REG(DC_DISP_SD_CONTROL); | ||
865 | NVSD_PRINT_REG(DC_DISP_SD_CSC_COEFF); | ||
866 | NVSD_PRINT_REG_ARRAY(DC_DISP_SD_LUT); | ||
867 | NVSD_PRINT_REG(DC_DISP_SD_FLICKER_CONTROL); | ||
868 | NVSD_PRINT_REG(DC_DISP_SD_PIXEL_COUNT); | ||
869 | NVSD_PRINT_REG_ARRAY(DC_DISP_SD_HISTOGRAM); | ||
870 | NVSD_PRINT_REG(DC_DISP_SD_BL_PARAMETERS); | ||
871 | NVSD_PRINT_REG_ARRAY(DC_DISP_SD_BL_TF); | ||
872 | NVSD_PRINT_REG(DC_DISP_SD_BL_CONTROL); | ||
873 | NVSD_PRINT_REG(DC_DISP_SD_HW_K_VALUES); | ||
874 | NVSD_PRINT_REG(DC_DISP_SD_MAN_K_VALUES); | ||
875 | |||
876 | return res; | ||
877 | } | ||
878 | |||
879 | /* Sysfs initializer */ | ||
880 | int nvsd_create_sysfs(struct device *dev) | ||
881 | { | ||
882 | int retval = 0; | ||
883 | |||
884 | nvsd_kobj = kobject_create_and_add("smartdimmer", &dev->kobj); | ||
885 | |||
886 | if (!nvsd_kobj) | ||
887 | return -ENOMEM; | ||
888 | |||
889 | retval = sysfs_create_group(nvsd_kobj, &nvsd_attr_group); | ||
890 | |||
891 | if (retval) { | ||
892 | kobject_put(nvsd_kobj); | ||
893 | dev_err(dev, "%s: failed to create attributes\n", __func__); | ||
894 | } | ||
895 | |||
896 | return retval; | ||
897 | } | ||
898 | |||
899 | /* Sysfs destructor */ | ||
900 | void __devexit nvsd_remove_sysfs(struct device *dev) | ||
901 | { | ||
902 | if (nvsd_kobj) { | ||
903 | sysfs_remove_group(nvsd_kobj, &nvsd_attr_group); | ||
904 | kobject_put(nvsd_kobj); | ||
905 | } | ||
906 | } | ||