video: tegra: dc: power optimize DC and host1x clk
[linux-2.6.git] / drivers / video / tegra / dc / nvsd.c
1 /*
2  * drivers/video/tegra/dc/nvsd.c
3  *
4  * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved.
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
167                         if (target_k != cur_k) {
168                                 if (target_k > cur_k)
169                                         cur_k += K_STEP;
170                                 else
171                                         cur_k -= K_STEP;
172                         }
173
174                         /* Set manual k value */
175                         man_k = SD_MAN_K_R(cur_k) |
176                                 SD_MAN_K_G(cur_k) | SD_MAN_K_B(cur_k);
177                         tegra_dc_io_start(dc);
178                         tegra_dc_writel(dc, man_k, DC_DISP_SD_MAN_K_VALUES);
179                         tegra_dc_io_end(dc);
180                         /* Set manual brightness value */
181                         atomic_set(sd_brightness, cur_sd_brightness);
182                 }
183                 settings->phase_adj_step = step;
184                 return true;
185         } else
186                 return false;
187 }
188
189 /* phase in the luts based on the current and max step */
190 static void nvsd_phase_in_luts(struct tegra_dc_sd_settings *settings,
191         struct tegra_dc *dc)
192 {
193         u32 val;
194         u8 bw_idx;
195         int i;
196         u16 phase_settings_step = settings->phase_settings_step;
197         u16 num_phase_in_steps = settings->num_phase_in_steps;
198
199         bw_idx = nvsd_get_bw_idx(settings);
200
201         /* Phase in Final LUT */
202         for (i = 0; i < DC_DISP_SD_LUT_NUM; i++) {
203                 val = SD_LUT_R((settings->lut[bw_idx][i].r *
204                                 phase_settings_step)/num_phase_in_steps) |
205                         SD_LUT_G((settings->lut[bw_idx][i].g *
206                                 phase_settings_step)/num_phase_in_steps) |
207                         SD_LUT_B((settings->lut[bw_idx][i].b *
208                                 phase_settings_step)/num_phase_in_steps);
209
210                 tegra_dc_writel(dc, val, DC_DISP_SD_LUT(i));
211         }
212         /* Phase in Final BLTF */
213         for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) {
214                 val = SD_BL_TF_POINT_0(255-((255-settings->bltf[bw_idx][i][0])
215                                 * phase_settings_step)/num_phase_in_steps) |
216                         SD_BL_TF_POINT_1(255-((255-settings->bltf[bw_idx][i][1])
217                                 * phase_settings_step)/num_phase_in_steps) |
218                         SD_BL_TF_POINT_2(255-((255-settings->bltf[bw_idx][i][2])
219                                 * phase_settings_step)/num_phase_in_steps) |
220                         SD_BL_TF_POINT_3(255-((255-settings->bltf[bw_idx][i][3])
221                                 * phase_settings_step)/num_phase_in_steps);
222
223                 tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i));
224         }
225 }
226
227 /* handle the commands that may be invoked for phase_in_settings */
228 static void nvsd_cmd_handler(struct tegra_dc_sd_settings *settings,
229         struct tegra_dc *dc)
230 {
231         u32 val;
232         u8 bw_idx, bw;
233
234         if (settings->cmd & ENABLE) {
235                 settings->phase_settings_step++;
236                 if (settings->phase_settings_step >=
237                                 settings->num_phase_in_steps)
238                         settings->cmd &= ~ENABLE;
239
240                 nvsd_phase_in_luts(settings, dc);
241         }
242         if (settings->cmd & DISABLE) {
243                 settings->phase_settings_step--;
244                 nvsd_phase_in_luts(settings, dc);
245                 if (settings->phase_settings_step == 0) {
246                         /* finish up aggressiveness phase in */
247                         if (settings->cmd & AGG_CHG)
248                                 settings->aggressiveness = settings->final_agg;
249                         settings->cmd = NO_CMD;
250                         settings->enable = 0;
251                         nvsd_init(dc, settings);
252                 }
253         }
254         if (settings->cmd & AGG_CHG) {
255                 if (settings->aggressiveness == settings->final_agg)
256                         settings->cmd &= ~AGG_CHG;
257                 if ((settings->cur_agg_step++ & (STEPS_PER_AGG_CHG - 1)) == 0) {
258                         settings->final_agg > settings->aggressiveness ?
259                                 settings->aggressiveness++ :
260                                 settings->aggressiveness--;
261
262                         /* Update aggressiveness value in HW */
263                         val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL);
264                         val &= ~SD_AGGRESSIVENESS(0x7);
265                         val |= SD_AGGRESSIVENESS(settings->aggressiveness);
266
267                         /* Adjust bin_width for automatic setting */
268                         if (settings->bin_width == -1) {
269                                 bw_idx = nvsd_get_bw_idx(settings);
270
271                                 bw = bw_idx << 3;
272
273                                 val &= ~SD_BIN_WIDTH_MASK;
274                                 val |= bw;
275                         }
276
277                         tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL);
278
279                         nvsd_phase_in_luts(settings, dc);
280                 }
281         }
282 }
283
284 static bool nvsd_update_enable(struct tegra_dc_sd_settings *settings,
285         int enable_val)
286 {
287
288         if (enable_val != 1 && enable_val != 0)
289                 return false;
290
291         if (!settings->cmd && settings->enable != enable_val) {
292                 settings->num_phase_in_steps =
293                         STEPS_PER_AGG_LVL*settings->aggressiveness;
294                 settings->phase_settings_step = enable_val ?
295                         0 : settings->num_phase_in_steps;
296         }
297
298         if (settings->enable != enable_val || settings->cmd & DISABLE) {
299                 settings->cmd &= ~(ENABLE | DISABLE);
300                 if (!settings->enable && enable_val)
301                         settings->cmd |= PHASE_IN;
302                 settings->cmd |= enable_val ? ENABLE : DISABLE;
303                 return true;
304         }
305
306         return false;
307 }
308
309 static bool nvsd_update_agg(struct tegra_dc_sd_settings *settings, int agg_val)
310 {
311         int i;
312         int pri_lvl = SD_AGG_PRI_LVL(agg_val);
313         int agg_lvl = SD_GET_AGG(agg_val);
314         struct tegra_dc_sd_agg_priorities *sd_agg_priorities =
315                 &settings->agg_priorities;
316
317         if (agg_lvl > 5 || agg_lvl < 0)
318                 return false;
319         else if (agg_lvl == 0 && pri_lvl == 0)
320                 return false;
321
322         if (pri_lvl >= 0 && pri_lvl < 4)
323                 sd_agg_priorities->agg[pri_lvl] = agg_lvl;
324
325         for (i = NUM_AGG_PRI_LVLS - 1; i >= 0; i--) {
326                 if (sd_agg_priorities->agg[i])
327                         break;
328         }
329
330         sd_agg_priorities->pri_lvl = i;
331         pri_lvl = i;
332         agg_lvl = sd_agg_priorities->agg[i];
333
334         if (settings->phase_in_settings && settings->enable &&
335                 settings->aggressiveness != agg_lvl) {
336
337                 settings->final_agg = agg_lvl;
338                 settings->cmd |= AGG_CHG;
339                 settings->cur_agg_step = 0;
340                 return true;
341         } else if (settings->aggressiveness != agg_lvl) {
342                 settings->aggressiveness = agg_lvl;
343                 return true;
344         }
345
346         return false;
347 }
348
349 /* Functional initialization */
350 void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings)
351 {
352         u32 i = 0;
353         u32 val = 0;
354         u32 bw_idx = 0;
355         /* TODO: check if HW says SD's available */
356
357         tegra_dc_io_start(dc);
358         /* If SD's not present or disabled, clear the register and return. */
359         if (!settings || settings->enable == 0) {
360                 /* clear the brightness val, too. */
361                 if (sd_brightness)
362                         atomic_set(sd_brightness, 255);
363
364                 sd_brightness = NULL;
365
366                 if (settings)
367                         settings->phase_settings_step = 0;
368                 tegra_dc_writel(dc, 0, DC_DISP_SD_CONTROL);
369                 tegra_dc_io_end(dc);
370                 return;
371         }
372
373         dev_dbg(&dc->ndev->dev, "NVSD Init:\n");
374
375         /* init agg_priorities */
376         if (!settings->agg_priorities.agg[0])
377                 settings->agg_priorities.agg[0] = settings->aggressiveness;
378
379         /* WAR: Settings will not be valid until the next flip.
380          * Thus, set manual K to either HW's current value (if
381          * we're already enabled) or a non-effective value (if
382          * we're about to enable). */
383         val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL);
384
385         if (val & SD_ENABLE_NORMAL)
386                 if (settings->phase_in_adjustments)
387                         i = tegra_dc_readl(dc, DC_DISP_SD_MAN_K_VALUES);
388                 else
389                         i = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES);
390         else
391                 i = 0; /* 0 values for RGB = 1.0, i.e. non-affected */
392
393         tegra_dc_writel(dc, i, DC_DISP_SD_MAN_K_VALUES);
394         /* Enable manual correction mode here so that changing the
395          * settings won't immediately impact display dehavior. */
396         val |= SD_CORRECTION_MODE_MAN;
397         tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL);
398
399         bw_idx = nvsd_get_bw_idx(settings);
400
401         /* Write LUT */
402         if (!settings->cmd) {
403                 dev_dbg(&dc->ndev->dev, "  LUT:\n");
404
405                 for (i = 0; i < DC_DISP_SD_LUT_NUM; i++) {
406                         val = SD_LUT_R(settings->lut[bw_idx][i].r) |
407                                 SD_LUT_G(settings->lut[bw_idx][i].g) |
408                                 SD_LUT_B(settings->lut[bw_idx][i].b);
409                         tegra_dc_writel(dc, val, DC_DISP_SD_LUT(i));
410
411                         dev_dbg(&dc->ndev->dev, "    %d: 0x%08x\n", i, val);
412                 }
413         }
414
415         /* Write BL TF */
416         if (!settings->cmd) {
417                 dev_dbg(&dc->ndev->dev, "  BL_TF:\n");
418
419                 for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) {
420                         val = SD_BL_TF_POINT_0(settings->bltf[bw_idx][i][0]) |
421                                 SD_BL_TF_POINT_1(settings->bltf[bw_idx][i][1]) |
422                                 SD_BL_TF_POINT_2(settings->bltf[bw_idx][i][2]) |
423                                 SD_BL_TF_POINT_3(settings->bltf[bw_idx][i][3]);
424
425                         tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i));
426
427                         dev_dbg(&dc->ndev->dev, "    %d: 0x%08x\n", i, val);
428                 }
429         } else if ((settings->cmd & PHASE_IN)) {
430                 settings->cmd &= ~PHASE_IN;
431                 /* Write NO_OP values for BLTF */
432                 for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) {
433                         val = SD_BL_TF_POINT_0(0xFF) |
434                                 SD_BL_TF_POINT_1(0xFF) |
435                                 SD_BL_TF_POINT_2(0xFF) |
436                                 SD_BL_TF_POINT_3(0xFF);
437
438                         tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i));
439
440                         dev_dbg(&dc->ndev->dev, "    %d: 0x%08x\n", i, val);
441                 }
442         }
443
444         /* Set step correctly on init */
445         if (!settings->cmd && settings->phase_in_settings) {
446                 settings->num_phase_in_steps = STEPS_PER_AGG_LVL *
447                         settings->aggressiveness;
448                 settings->phase_settings_step = settings->enable ?
449                         settings->num_phase_in_steps : 0;
450         }
451
452         /* Write Coeff */
453         val = SD_CSC_COEFF_R(settings->coeff.r) |
454                 SD_CSC_COEFF_G(settings->coeff.g) |
455                 SD_CSC_COEFF_B(settings->coeff.b);
456         tegra_dc_writel(dc, val, DC_DISP_SD_CSC_COEFF);
457         dev_dbg(&dc->ndev->dev, "  COEFF: 0x%08x\n", val);
458
459         /* Write BL Params */
460         val = SD_BLP_TIME_CONSTANT(settings->blp.time_constant) |
461                 SD_BLP_STEP(settings->blp.step);
462         tegra_dc_writel(dc, val, DC_DISP_SD_BL_PARAMETERS);
463         dev_dbg(&dc->ndev->dev, "  BLP: 0x%08x\n", val);
464
465         /* Write Auto/Manual PWM */
466         val = (settings->use_auto_pwm) ? SD_BLC_MODE_AUTO : SD_BLC_MODE_MAN;
467         tegra_dc_writel(dc, val, DC_DISP_SD_BL_CONTROL);
468         dev_dbg(&dc->ndev->dev, "  BL_CONTROL: 0x%08x\n", val);
469
470         /* Write Flicker Control */
471         val = SD_FC_TIME_LIMIT(settings->fc.time_limit) |
472                 SD_FC_THRESHOLD(settings->fc.threshold);
473         tegra_dc_writel(dc, val, DC_DISP_SD_FLICKER_CONTROL);
474         dev_dbg(&dc->ndev->dev, "  FLICKER_CONTROL: 0x%08x\n", val);
475
476         /* Manage SD Control */
477         val = 0;
478         /* Stay in manual correction mode until the next flip. */
479         val |= SD_CORRECTION_MODE_MAN;
480         /* Enable / One-Shot */
481         val |= (settings->enable == 2) ?
482                 (SD_ENABLE_ONESHOT | SD_ONESHOT_ENABLE) :
483                 SD_ENABLE_NORMAL;
484         /* HW Update Delay */
485         val |= SD_HW_UPDATE_DLY(settings->hw_update_delay);
486         /* Video Luma */
487         val |= (settings->use_vid_luma) ? SD_USE_VID_LUMA : 0;
488         /* Aggressiveness */
489         val |= SD_AGGRESSIVENESS(settings->aggressiveness);
490         /* Bin Width (value derived from bw_idx) */
491         val |= bw_idx << 3;
492         /* Finally, Write SD Control */
493         tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL);
494         dev_dbg(&dc->ndev->dev, "  SD_CONTROL: 0x%08x\n", val);
495         tegra_dc_io_end(dc);
496
497         /* set the brightness pointer */
498         sd_brightness = settings->sd_brightness;
499
500         /* note that we're in manual K until the next flip */
501         atomic_set(&man_k_until_blank, 1);
502 }
503
504 /* Periodic update */
505 bool nvsd_update_brightness(struct tegra_dc *dc)
506 {
507         u32 val = 0;
508         int cur_sd_brightness;
509         struct tegra_dc_sd_settings *settings = dc->out->sd_settings;
510
511         if (sd_brightness) {
512                 if (atomic_read(&man_k_until_blank) &&
513                                         !settings->phase_in_adjustments) {
514                         val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL);
515                         val &= ~SD_CORRECTION_MODE_MAN;
516                         tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL);
517                         atomic_set(&man_k_until_blank, 0);
518                 }
519
520                 if (settings->cmd)
521                         nvsd_cmd_handler(settings, dc);
522
523                 /* nvsd_cmd_handler may turn off didim */
524                 if (!settings->enable)
525                         return true;
526
527                 cur_sd_brightness = atomic_read(sd_brightness);
528
529                 /* read brightness value */
530                 val = tegra_dc_readl(dc, DC_DISP_SD_BL_CONTROL);
531                 val = SD_BLC_BRIGHTNESS(val);
532
533                 if (settings->phase_in_adjustments) {
534                         return nvsd_phase_in_adjustments(dc, settings);
535                 } else if (val != (u32)cur_sd_brightness) {
536                         /* set brightness value and note the update */
537                         atomic_set(sd_brightness, (int)val);
538                         return true;
539                 }
540         }
541
542         /* No update needed. */
543         return false;
544 }
545
546 static ssize_t nvsd_lut_show(struct tegra_dc_sd_settings *sd_settings,
547         char *buf, ssize_t res)
548 {
549         u32 i;
550         u32 j;
551
552         for (i = 0; i < NUM_BIN_WIDTHS; i++) {
553                 res += snprintf(buf + res, PAGE_SIZE - res,
554                         "Bin Width: %d\n", 1 << i);
555
556                 for (j = 0; j < DC_DISP_SD_LUT_NUM; j++) {
557                         res += snprintf(buf + res,
558                                 PAGE_SIZE - res,
559                                 "%d: R: %3d / G: %3d / B: %3d\n",
560                                 j,
561                                 sd_settings->lut[i][j].r,
562                                 sd_settings->lut[i][j].g,
563                                 sd_settings->lut[i][j].b);
564                 }
565         }
566         return res;
567 }
568
569 static ssize_t nvsd_bltf_show(struct tegra_dc_sd_settings *sd_settings,
570         char *buf, ssize_t res)
571 {
572         u32 i;
573         u32 j;
574
575         for (i = 0; i < NUM_BIN_WIDTHS; i++) {
576                 res += snprintf(buf + res, PAGE_SIZE - res,
577                         "Bin Width: %d\n", 1 << i);
578
579                 for (j = 0; j < DC_DISP_SD_BL_TF_NUM; j++) {
580                         res += snprintf(buf + res,
581                                 PAGE_SIZE - res,
582                                 "%d: 0: %3d / 1: %3d / 2: %3d / 3: %3d\n",
583                                 j,
584                                 sd_settings->bltf[i][j][0],
585                                 sd_settings->bltf[i][j][1],
586                                 sd_settings->bltf[i][j][2],
587                                 sd_settings->bltf[i][j][3]);
588                 }
589         }
590         return res;
591 }
592
593 /* Sysfs accessors */
594 static ssize_t nvsd_settings_show(struct kobject *kobj,
595         struct kobj_attribute *attr, char *buf)
596 {
597         struct device *dev = container_of((kobj->parent), struct device, kobj);
598         struct nvhost_device *ndev = to_nvhost_device(dev);
599         struct tegra_dc *dc = nvhost_get_drvdata(ndev);
600         struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings;
601         ssize_t res = 0;
602
603         if (sd_settings) {
604                 if (IS_NVSD_ATTR(enable))
605                         res = snprintf(buf, PAGE_SIZE, "%d\n",
606                                 sd_settings->enable);
607                 else if (IS_NVSD_ATTR(aggressiveness))
608                         res = snprintf(buf, PAGE_SIZE, "%d\n",
609                                 sd_settings->aggressiveness);
610                 else if (IS_NVSD_ATTR(phase_in_settings))
611                         res = snprintf(buf, PAGE_SIZE, "%d\n",
612                                 sd_settings->phase_in_settings);
613                 else if (IS_NVSD_ATTR(phase_in_adjustments))
614                         res = snprintf(buf, PAGE_SIZE, "%d\n",
615                                 sd_settings->phase_in_adjustments);
616                 else if (IS_NVSD_ATTR(bin_width))
617                         res = snprintf(buf, PAGE_SIZE, "%d\n",
618                                 sd_settings->bin_width);
619                 else if (IS_NVSD_ATTR(hw_update_delay))
620                         res = snprintf(buf, PAGE_SIZE, "%d\n",
621                                 sd_settings->hw_update_delay);
622                 else if (IS_NVSD_ATTR(use_vid_luma))
623                         res = snprintf(buf, PAGE_SIZE, "%d\n",
624                                 sd_settings->use_vid_luma);
625                 else if (IS_NVSD_ATTR(coeff))
626                         res = snprintf(buf, PAGE_SIZE,
627                                 "R: %d / G: %d / B: %d\n",
628                                 sd_settings->coeff.r,
629                                 sd_settings->coeff.g,
630                                 sd_settings->coeff.b);
631                 else if (IS_NVSD_ATTR(blp_time_constant))
632                         res = snprintf(buf, PAGE_SIZE, "%d\n",
633                                 sd_settings->blp.time_constant);
634                 else if (IS_NVSD_ATTR(blp_step))
635                         res = snprintf(buf, PAGE_SIZE, "%d\n",
636                                 sd_settings->blp.step);
637                 else if (IS_NVSD_ATTR(fc_time_limit))
638                         res = snprintf(buf, PAGE_SIZE, "%d\n",
639                                 sd_settings->fc.time_limit);
640                 else if (IS_NVSD_ATTR(fc_threshold))
641                         res = snprintf(buf, PAGE_SIZE, "%d\n",
642                                 sd_settings->fc.threshold);
643                 else if (IS_NVSD_ATTR(lut))
644                         res = nvsd_lut_show(sd_settings, buf, res);
645                 else if (IS_NVSD_ATTR(bltf))
646                         res = nvsd_bltf_show(sd_settings, buf, res);
647                 else
648                         res = -EINVAL;
649         } else {
650                 /* This shouldn't be reachable. But just in case... */
651                 res = -EINVAL;
652         }
653
654         return res;
655 }
656
657 #define nvsd_check_and_update(_min, _max, _varname) { \
658         int val = simple_strtol(buf, NULL, 10); \
659         if (val >= _min && val <= _max) { \
660                 sd_settings->_varname = val; \
661                 settings_updated = true; \
662         } }
663
664 #define nvsd_get_multi(_ele, _num, _act, _min, _max) { \
665         char *b, *c, *orig_b; \
666         b = orig_b = kstrdup(buf, GFP_KERNEL); \
667         for (_act = 0; _act < _num; _act++) { \
668                 if (!b) \
669                         break; \
670                 b = strim(b); \
671                 c = strsep(&b, " "); \
672                 if (!strlen(c)) \
673                         break; \
674                 _ele[_act] = simple_strtol(c, NULL, 10); \
675                 if (_ele[_act] < _min || _ele[_act] > _max) \
676                         break; \
677         } \
678         if (orig_b) \
679                 kfree(orig_b); \
680 }
681
682 static int nvsd_lut_store(struct tegra_dc_sd_settings *sd_settings,
683         const char *buf)
684 {
685         int ele[3 * DC_DISP_SD_LUT_NUM * NUM_BIN_WIDTHS];
686         int i = 0;
687         int j = 0;
688         int num = 3 * DC_DISP_SD_LUT_NUM * NUM_BIN_WIDTHS;
689
690         nvsd_get_multi(ele, num, i, 0, 255);
691
692         if (i != num)
693                 return -EINVAL;
694
695         for (i = 0; i < NUM_BIN_WIDTHS; i++) {
696                 for (j = 0; j < DC_DISP_SD_LUT_NUM; j++) {
697                         sd_settings->lut[i][j].r =
698                                 ele[i * NUM_BIN_WIDTHS + j * 3 + 0];
699                         sd_settings->lut[i][j].g =
700                                 ele[i * NUM_BIN_WIDTHS + j * 3 + 1];
701                         sd_settings->lut[i][j].b =
702                                 ele[i * NUM_BIN_WIDTHS + j * 3 + 2];
703                 }
704         }
705         return 0;
706 }
707
708 static int nvsd_bltf_store(struct tegra_dc_sd_settings *sd_settings,
709         const char *buf)
710 {
711         int ele[4 * DC_DISP_SD_BL_TF_NUM * NUM_BIN_WIDTHS];
712         int i = 0, j = 0, num = ARRAY_SIZE(ele);
713
714         nvsd_get_multi(ele, num, i, 0, 255);
715
716         if (i != num)
717                 return -EINVAL;
718
719         for (i = 0; i < NUM_BIN_WIDTHS; i++) {
720                 for (j = 0; j < DC_DISP_SD_BL_TF_NUM; j++) {
721                         size_t base = (i * NUM_BIN_WIDTHS *
722                                        DC_DISP_SD_BL_TF_NUM) + (j * 4);
723                         sd_settings->bltf[i][j][0] = ele[base + 0];
724                         sd_settings->bltf[i][j][1] = ele[base + 1];
725                         sd_settings->bltf[i][j][2] = ele[base + 2];
726                         sd_settings->bltf[i][j][3] = ele[base + 3];
727                 }
728         }
729
730         return 0;
731 }
732
733 static ssize_t nvsd_settings_store(struct kobject *kobj,
734         struct kobj_attribute *attr, const char *buf, size_t count)
735 {
736         struct device *dev = container_of((kobj->parent), struct device, kobj);
737         struct nvhost_device *ndev = to_nvhost_device(dev);
738         struct tegra_dc *dc = nvhost_get_drvdata(ndev);
739         struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings;
740         ssize_t res = count;
741         bool settings_updated = false;
742         long int result;
743         int err;
744
745         if (sd_settings) {
746                 if (IS_NVSD_ATTR(enable)) {
747                         if (sd_settings->phase_in_settings) {
748                                 err = strict_strtol(buf, 10, &result);
749                                 if (err)
750                                         return err;
751
752                                 if (nvsd_update_enable(sd_settings, result))
753                                         nvsd_check_and_update(1, 1, enable);
754
755                         } else {
756                                 nvsd_check_and_update(0, 1, enable);
757                         }
758                 } else if (IS_NVSD_ATTR(aggressiveness)) {
759                         err = strict_strtol(buf, 10, &result);
760                         if (err)
761                                 return err;
762
763                         if (nvsd_update_agg(sd_settings, result)
764                                         && !sd_settings->phase_in_settings)
765                                 settings_updated = true;
766
767                 } else if (IS_NVSD_ATTR(phase_in_settings)) {
768                         nvsd_check_and_update(0, 1, phase_in_settings);
769                 } else if (IS_NVSD_ATTR(phase_in_adjustments)) {
770                         nvsd_check_and_update(0, 1, phase_in_adjustments);
771                 } else if (IS_NVSD_ATTR(bin_width)) {
772                         nvsd_check_and_update(0, 8, bin_width);
773                 } else if (IS_NVSD_ATTR(hw_update_delay)) {
774                         nvsd_check_and_update(0, 2, hw_update_delay);
775                 } else if (IS_NVSD_ATTR(use_vid_luma)) {
776                         nvsd_check_and_update(0, 1, use_vid_luma);
777                 } else if (IS_NVSD_ATTR(coeff)) {
778                         int ele[3], i = 0, num = 3;
779                         nvsd_get_multi(ele, num, i, 0, 15);
780
781                         if (i == num) {
782                                 sd_settings->coeff.r = ele[0];
783                                 sd_settings->coeff.g = ele[1];
784                                 sd_settings->coeff.b = ele[2];
785                                 settings_updated = true;
786                         } else {
787                                 res = -EINVAL;
788                         }
789                 } else if (IS_NVSD_ATTR(blp_time_constant)) {
790                         nvsd_check_and_update(0, 1024, blp.time_constant);
791                 } else if (IS_NVSD_ATTR(blp_step)) {
792                         nvsd_check_and_update(0, 255, blp.step);
793                 } else if (IS_NVSD_ATTR(fc_time_limit)) {
794                         nvsd_check_and_update(0, 255, fc.time_limit);
795                 } else if (IS_NVSD_ATTR(fc_threshold)) {
796                         nvsd_check_and_update(0, 255, fc.threshold);
797                 } else if (IS_NVSD_ATTR(lut)) {
798                         if (nvsd_lut_store(sd_settings, buf))
799                                 res = -EINVAL;
800                         else
801                                 settings_updated = true;
802                 } else if (IS_NVSD_ATTR(bltf)) {
803                         if (nvsd_bltf_store(sd_settings, buf))
804                                 res = -EINVAL;
805                         else
806                                 settings_updated = true;
807                 } else {
808                         res = -EINVAL;
809                 }
810
811                 /* Re-init if our settings were updated. */
812                 if (settings_updated) {
813                         mutex_lock(&dc->lock);
814                         if (!dc->enabled) {
815                                 mutex_unlock(&dc->lock);
816                                 return -ENODEV;
817                         }
818
819                         tegra_dc_io_start(dc);
820                         tegra_dc_hold_dc_out(dc);
821                         nvsd_init(dc, sd_settings);
822                         tegra_dc_release_dc_out(dc);
823                         tegra_dc_io_end(dc);
824
825                         mutex_unlock(&dc->lock);
826
827                         /* Update backlight state IFF we're disabling! */
828                         if (!sd_settings->enable && sd_settings->bl_device) {
829                                 /* Do the actual brightness update outside of
830                                  * the mutex */
831                                 struct platform_device *pdev =
832                                         sd_settings->bl_device;
833                                 struct backlight_device *bl =
834                                         platform_get_drvdata(pdev);
835
836                                 if (bl)
837                                         backlight_update_status(bl);
838                         }
839                 }
840         } else {
841                 /* This shouldn't be reachable. But just in case... */
842                 res = -EINVAL;
843         }
844
845         return res;
846 }
847
848 #define NVSD_PRINT_REG(__name) { \
849         u32 val = tegra_dc_readl(dc, __name); \
850         res += snprintf(buf + res, PAGE_SIZE - res, #__name ": 0x%08x\n", \
851                 val); \
852 }
853
854 #define NVSD_PRINT_REG_ARRAY(__name) { \
855         u32 val = 0, i = 0; \
856         res += snprintf(buf + res, PAGE_SIZE - res, #__name ":\n"); \
857         for (i = 0; i < __name##_NUM; i++) { \
858                 val = tegra_dc_readl(dc, __name(i)); \
859                 res += snprintf(buf + res, PAGE_SIZE - res, "  %d: 0x%08x\n", \
860                         i, val); \
861         } \
862 }
863
864 static ssize_t nvsd_registers_show(struct kobject *kobj,
865         struct kobj_attribute *attr, char *buf)
866 {
867         struct device *dev = container_of((kobj->parent), struct device, kobj);
868         struct nvhost_device *ndev = to_nvhost_device(dev);
869         struct tegra_dc *dc = nvhost_get_drvdata(ndev);
870         ssize_t res = 0;
871
872         mutex_lock(&dc->lock);
873         if (!dc->enabled) {
874                 mutex_unlock(&dc->lock);
875                 return -ENODEV;
876         }
877
878         mutex_unlock(&dc->lock);
879         NVSD_PRINT_REG(DC_DISP_SD_CONTROL);
880         NVSD_PRINT_REG(DC_DISP_SD_CSC_COEFF);
881         NVSD_PRINT_REG_ARRAY(DC_DISP_SD_LUT);
882         NVSD_PRINT_REG(DC_DISP_SD_FLICKER_CONTROL);
883         NVSD_PRINT_REG(DC_DISP_SD_PIXEL_COUNT);
884         NVSD_PRINT_REG_ARRAY(DC_DISP_SD_HISTOGRAM);
885         NVSD_PRINT_REG(DC_DISP_SD_BL_PARAMETERS);
886         NVSD_PRINT_REG_ARRAY(DC_DISP_SD_BL_TF);
887         NVSD_PRINT_REG(DC_DISP_SD_BL_CONTROL);
888         NVSD_PRINT_REG(DC_DISP_SD_HW_K_VALUES);
889         NVSD_PRINT_REG(DC_DISP_SD_MAN_K_VALUES);
890
891         return res;
892 }
893
894 /* Sysfs initializer */
895 int nvsd_create_sysfs(struct device *dev)
896 {
897         int retval = 0;
898
899         nvsd_kobj = kobject_create_and_add("smartdimmer", &dev->kobj);
900
901         if (!nvsd_kobj)
902                 return -ENOMEM;
903
904         retval = sysfs_create_group(nvsd_kobj, &nvsd_attr_group);
905
906         if (retval) {
907                 kobject_put(nvsd_kobj);
908                 dev_err(dev, "%s: failed to create attributes\n", __func__);
909         }
910
911         return retval;
912 }
913
914 /* Sysfs destructor */
915 void __devexit nvsd_remove_sysfs(struct device *dev)
916 {
917         if (nvsd_kobj) {
918                 sysfs_remove_group(nvsd_kobj, &nvsd_attr_group);
919                 kobject_put(nvsd_kobj);
920         }
921 }