aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/tegra/dc/nvsd.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/video/tegra/dc/nvsd.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/video/tegra/dc/nvsd.c')
-rw-r--r--drivers/video/tegra/dc/nvsd.c906
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
35static ssize_t nvsd_settings_show(struct kobject *kobj,
36 struct kobj_attribute *attr, char *buf);
37
38static ssize_t nvsd_settings_store(struct kobject *kobj,
39 struct kobj_attribute *attr, const char *buf, size_t count);
40
41static ssize_t nvsd_registers_show(struct kobject *kobj,
42 struct kobj_attribute *attr, char *buf);
43
44NVSD_ATTR(enable);
45NVSD_ATTR(aggressiveness);
46NVSD_ATTR(phase_in_settings);
47NVSD_ATTR(phase_in_adjustments);
48NVSD_ATTR(bin_width);
49NVSD_ATTR(hw_update_delay);
50NVSD_ATTR(use_vid_luma);
51NVSD_ATTR(coeff);
52NVSD_ATTR(blp_time_constant);
53NVSD_ATTR(blp_step);
54NVSD_ATTR(fc_time_limit);
55NVSD_ATTR(fc_threshold);
56NVSD_ATTR(lut);
57NVSD_ATTR(bltf);
58static struct kobj_attribute nvsd_attr_registers =
59 __ATTR(registers, S_IRUGO, nvsd_registers_show, NULL);
60
61static 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
80static struct attribute_group nvsd_attr_group = {
81 .attrs = nvsd_attrs,
82};
83
84static struct kobject *nvsd_kobj;
85
86/* shared brightness variable */
87static atomic_t *sd_brightness = NULL;
88/* shared boolean for manual K workaround */
89static atomic_t man_k_until_blank = ATOMIC_INIT(0);
90
91static 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
134static 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 */
187static 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 */
225static 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
280static 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
305static 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 */
346void 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 */
495bool 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
536static 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
559static 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 */
584static 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
672static 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
698static 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
723static 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
849static 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 */
880int 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 */
900void __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}