forked from nemomobile/kernel-adaptation-n950-n9
-
Notifications
You must be signed in to change notification settings - Fork 5
/
panel-nokia-dsi.c
3803 lines (3035 loc) · 80.2 KB
/
panel-nokia-dsi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Nokia DSI command mode panel driver
*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*#define DEBUG*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
#include <linux/regulator/consumer.h>
#include <linux/semaphore.h>
#include <linux/hrtimer.h>
#include <plat/display.h>
#include <plat/panel-nokia-dsi.h>
/* DSI Virtual channel. Hardcoded for now. */
#define TCH 0
#define DCS_NOP 0x00
#define DCS_READ_NUM_ERRORS 0x05
#define DCS_READ_POWER_MODE 0x0a
#define DCS_READ_MADCTL 0x0b
#define DCS_READ_PIXEL_FORMAT 0x0c
#define DCS_RDDSDR 0x0f
#define DCS_SLEEP_IN 0x10
#define DCS_SLEEP_OUT 0x11
#define DCS_PARTIAL_MODE_ON 0x12
#define DCS_NORMAL_MODE_ON 0x13
#define DCS_DISPLAY_OFF 0x28
#define DCS_DISPLAY_ON 0x29
#define DCS_COLUMN_ADDR 0x2a
#define DCS_PAGE_ADDR 0x2b
#define DCS_MEMORY_WRITE 0x2c
#define DCS_PARTIAL_AREA 0x30
#define DCS_TEAR_OFF 0x34
#define DCS_TEAR_ON 0x35
#define DCS_MEM_ACC_CTRL 0x36
#define DCS_IDLE_MODE_OFF 0x38
#define DCS_IDLE_MODE_ON 0x39
#define DCS_PIXEL_FORMAT 0x3a
#define DCS_WRITE_TEAR_SCANLINE 0x44
#define DCS_BRIGHTNESS 0x51
#define DCS_READ_BRIGHTNESS 0x52
#define DCS_WRITE_CTRL_DISPLAY 0x53
#define DCS_READ_CTRL_DISPLAY 0x54
#define DCS_WRITE_CABC 0x55
#define DCS_READ_CABC 0x56
#define DCS_GET_ID1 0xda
#define DCS_GET_ID2 0xdb
#define DCS_GET_ID3 0xdc
#define MEASURE_PERF
/*#define MEASURE_PERF_SHOW_ALWAYS*/
enum pnd_ctrl_display_bits {
PND_CTRLD_BL = 1 << 2,
PND_CTRLD_DD = 1 << 3,
PND_CTRLD_BCTRL = 1 << 5,
PND_CTRLD_HBM_SHIFT = 6,
PND_CTRLD_HBM = 3 << PND_CTRLD_HBM_SHIFT,
};
/* High Brightness Mode luminance levels in cd/m^2 for DCS_WRITE_CTRL_DISPLAY */
enum pnd_hbm {
PND_HBM_OFF = 0,
PND_HBM_400 = 1,
PND_HBM_450 = 2, /* equals PND_HBM_500 on SMD Pyrenees */
PND_HBM_500 = 3,
PND_HBM_MAX = PND_HBM_500,
};
enum pnd_acl_mode {
PND_ACL_OFF = 0x00,
PND_ACL_ON_80P = 0x10,
PND_ACL_ON_70P = 0x11,
PND_ACL_ON_60P = 0x12,
};
/* Bits of DCS_READ_MADCTL and DCS_MEM_ACC_CTRL commands */
enum pnd_madctl_bits {
PND_PAGE_ADDRESS_ORDER_BT = 1 << 7,
PND_COL_ADDRESS_ORDER_RL = 1 << 6,
PND_COL_ORDER_REVERSE = 1 << 5,
PND_REFRESH_BOTTOM_TOP = 1 << 4,
PND_BGR_ORDER = 1 << 3,
PND_REFRESH_RIGHT_LEFT = 1 << 2,
PND_SWITCH_BTW_SEGMENT_RAM = 1 << 1,
PND_SWITCH_BTW_COMMON_RAM = 1 << 0,
};
/* Bits of DCS_READ_POWER_MODE command */
enum pnd_power_mode_bits {
PND_BOOSTER_VOLTAGE = 1 << 7,
PND_IDLE_MODE = 1 << 6,
PND_PARTIAL_MODE = 1 << 5,
PND_SLEEP_OUT = 1 << 4,
PND_NORMAL_MODE = 1 << 3,
PND_DISPLAY_ON = 1 << 2,
};
static irqreturn_t pnd_te_isr(int irq, void *data);
static int _pnd_enable_te(struct omap_dss_device *dssdev, bool enable);
static void pnd_frame_timeout_work_callback(struct work_struct *work);
static enum hrtimer_restart pnd_timer_handler(struct hrtimer *handle);
static void pnd_set_timer(struct omap_dss_device *dssdev);
static void pnd_ulps_work(struct work_struct *work);
static int pnd_write_display_current_settings(struct omap_dss_device *dssdev);
static int pnd_power_on(struct omap_dss_device *dssdev);
static void pnd_power_off(struct omap_dss_device *dssdev);
static void pnd_hw_reset(struct omap_dss_device *dssdev);
static int pnd_panel_reset(struct omap_dss_device *dssdev);
enum pnd_update_te {
PND_UPD_TE_NONE,
PND_UPD_TE_EXT,
PND_UPD_TE_DSI,
};
enum pnd_update_mode {
PND_UPD_MODE_NORMAL, /* update in one part */
PND_UPD_MODE_RL, /* right first, then left */
PND_UPD_MODE_LR, /* left first, then right */
PND_UPD_MODE_BT, /* bottom first, then top */
PND_UPD_MODE_TB, /* top first, then bottom */
};
enum pnd_update_state {
PND_UPD_STATE_NONE,
PND_UPD_STATE_FRAME1_WAIT,
PND_UPD_STATE_FRAME1_ONGOING,
PND_UPD_STATE_FRAME2_WAIT,
PND_UPD_STATE_FRAME2_ONGOING,
};
struct panel_regulator {
struct regulator *regulator;
const char *name;
int min_uV;
int max_uV;
};
static void free_regulators(struct panel_regulator *regulators, int n)
{
int i;
for (i = 0; i < n; i++) {
/* disable/put in reverse order */
regulator_disable(regulators[n - i - 1].regulator);
regulator_put(regulators[n - i - 1].regulator);
}
}
static int init_regulators(struct omap_dss_device *dssdev,
struct panel_regulator *regulators, int n)
{
int r, i, v;
for (i = 0; i < n; i++) {
struct regulator *reg;
reg = regulator_get(&dssdev->dev, regulators[i].name);
if (IS_ERR(reg)) {
dev_err(&dssdev->dev, "failed to get regulator %s\n",
regulators[i].name);
r = PTR_ERR(reg);
goto err;
}
/* FIXME: better handling of fixed vs. variable regulators */
v = regulator_get_voltage(reg);
if (v < regulators[i].min_uV || v > regulators[i].max_uV) {
r = regulator_set_voltage(reg, regulators[i].min_uV,
regulators[i].max_uV);
if (r) {
dev_err(&dssdev->dev,
"failed to set regulator %s voltage\n",
regulators[i].name);
regulator_put(reg);
goto err;
}
}
r = regulator_enable(reg);
if (r) {
dev_err(&dssdev->dev, "failed to enable regulator %s\n",
regulators[i].name);
regulator_put(reg);
goto err;
}
regulators[i].regulator = reg;
}
return 0;
err:
free_regulators(regulators, i);
return r;
}
static struct panel_regulator himalaya_regulators[] = {
{
.name = "VPNL",
.min_uV = 2300000,
.max_uV = 4800000,
},
{
.name = "VDDI",
.min_uV = 1650000,
.max_uV = 1950000,
},
};
/**
* struct panel_config - panel configuration
* @name: panel name
* @type: panel type
* @panel_id: panel ID (ID3)
* @width: panel width in micrometers
* @height: panel height in micrometers
* @timings: panel resolution
* @sleep: various panel specific delays, passed to msleep() if non-zero
* @reset_sequence: reset sequence timings, passed to udelay() if non-zero
* @regulators: array of panel regulators
* @num_regulators: number of regulators in the array
*/
struct panel_config {
const char *name;
int type;
unsigned char panel_id;
u32 width;
u32 height;
struct omap_video_timings timings;
struct {
unsigned int sleep_in;
unsigned int sleep_out;
unsigned int hw_reset;
unsigned int enable_te;
} sleep;
struct {
unsigned int high;
unsigned int low;
} reset_sequence;
struct panel_regulator *regulators;
int num_regulators;
};
enum {
PANEL_TAAL,
PANEL_HIMALAYA,
PANEL_PYRENEES,
};
static struct panel_config panel_configs[] = {
{
.name = "taal",
.type = PANEL_TAAL,
.panel_id = 0x7d,
.width = 84240,
.height = 46800,
.timings = {
.x_res = 864,
.y_res = 480,
},
.sleep = {
.sleep_in = 5,
.sleep_out = 5,
.hw_reset = 5,
.enable_te = 100, /* possible panel bug */
},
.reset_sequence = {
.high = 10,
.low = 10,
},
},
{
.name = "himalaya",
.type = PANEL_HIMALAYA,
.panel_id = 0x8a,
.width = 48960,
.height = 88128,
.timings = {
.x_res = 480,
.y_res = 864,
.vfp = 58,
.vbp = 6,
},
.sleep = {
.sleep_in = 5,
.sleep_out = 5,
.hw_reset = 5,
},
.reset_sequence = {
.high = 10,
.low = 10,
},
.regulators = himalaya_regulators,
.num_regulators = ARRAY_SIZE(himalaya_regulators),
},
{
.name = "pyrenees",
.type = PANEL_PYRENEES,
.panel_id = 0x96,
.width = 48240,
.height = 86832,
.timings = {
.x_res = 480,
.y_res = 864,
.vsw = 188,
},
.sleep = {
.sleep_in = 5,
.sleep_out = 5,
.hw_reset = 5,
},
.reset_sequence = {
.high = 10,
.low = 10,
},
/* Reuse Himalaya regulator configuration */
.regulators = himalaya_regulators,
.num_regulators = ARRAY_SIZE(himalaya_regulators),
},
};
struct update_region {
u16 x;
u16 y;
u16 w;
u16 h;
};
/**
* struct pnd_data - panel runtime data
* @lock: lock protecting this structure
* @bldev: backlight device
* @hw_guard_end: next value of jiffies when we can issue the next
* sleep in/out command
* @hw_guard_wait: max guard time in jiffies
* @dssdev: DSS device pointer
* @id1: %DCS_GET_ID1 command response
* @id2: %DCS_GET_ID2 command response
* @id3: %DCS_GET_ID3 command response
* @enabled: if true, the panel is enabled from driver point of view
* @display_on: if true, the %DCS_DISPLAY_ON command has been issued
* @rotate: rotation in terms of the panel for %DCS_MEM_ACC_CTRL command
* @mirror: panel mirroring for %DCS_MEM_ACC_CTRL command
* @te_enabled: tearing effect line, see %DCS_TEAR_OFF and %DCS_TEAR_ON commands
* @wq: workqueue for ESD check, ULPS, two part update calibration, etc.
* @err_wq: independent workqueue for error handling (to avoid deadlocks)
* @esd_timeout: ESD check interval in ms, 0 for disabled
* @esd_work: ESD check work
* @ulps_enabled: if true, the panel is in Ultra Low Power State (ULPS)
* @ulps_timeout: timeout for entering ULPS in ms, 0 for disabled
* @ulps_work: ULPS enter work
* @lpm_enabled: if true, the panel is in Low Power Mode (LPM)
* @upd_state: two-part update state
* @original_update_region: update region for original update call
* @frame_timeout_work: timeout work for frame tranmission
* @update_te: tear effect in update, see &enum pnd_update_te
* @update_mode: update mode, see &enum pnd_update_mode
* @use_dsi_bl: if true, use %DCS_BRIGHTNESS command to adjust backlight
* @cabc_broken: if true, the panel CABC is broken
* @cabc_mode: CABC or ACL mode, depending on panel, for %DCS_WRITE_CABC command
* @display_dimming: if true, use dimming (fading) in backlight adjustment
* @hbm: High Brightness Mode (HBM) value for %DCS_WRITE_CTRL_DISPLAY command
* @intro_printed: if true, the driver startup message has been printed
* @panel_config: pointer to &struct panel_config of the panel
* @hrtimer: timer for two-part update
* @timer: HR timer value for mid display
* @calibration_work: work for starting calibration of frame_us
* @calib_lock: lock for the calibration related fields
* @calibrating: true if the frame_us value is being calibrated
* @nsamples: number of good samples measured
* @nbadsamples: number of bad (out of range) samples measured
* @frame_us_tot: total time for nsamples for calculating average
* @frame_us: measured frame time for calculating timer
* @time_prev: start time of previous te irq
*/
struct pnd_data {
struct semaphore lock;
struct backlight_device *bldev;
unsigned long hw_guard_end;
unsigned long hw_guard_wait;
struct omap_dss_device *dssdev;
unsigned char id1;
unsigned char id2;
unsigned char id3;
bool enabled;
bool display_on;
u8 rotate;
bool mirror;
bool te_enabled;
atomic_t te_irq_count;
struct workqueue_struct *wq;
struct workqueue_struct *err_wq;
unsigned esd_timeout;
struct delayed_work esd_work;
bool ulps_enabled;
unsigned ulps_timeout;
struct delayed_work ulps_work;
bool lpm_enabled;
atomic_t upd_state;
struct update_region original_update_region;
struct update_region update_region;
struct delayed_work frame_timeout_work;
enum pnd_update_te update_te;
enum pnd_update_mode update_mode;
bool use_dsi_bl;
bool cabc_broken;
unsigned cabc_mode;
bool display_dimming;
bool dimming_broken;
enum pnd_hbm hbm;
bool intro_printed;
struct panel_config *panel_config;
struct hrtimer hrtimer;
ktime_t timer;
bool reverse_te_irq_and_timer;
#ifdef MEASURE_PERF
struct {
/* 0 = update */
/* 1 = timer */
/* 2 = cb1 */
/* 3 = TE */
/* 4 = cb2 */
ktime_t phase[5];
} perf;
#endif
struct delayed_work calibration_work;
spinlock_t calib_lock;
bool calibrating;
int nsamples;
int nbadsamples;
u32 frame_us_tot;
u32 frame_us;
ktime_t time_prev;
};
static inline struct nokia_dsi_panel_data
*get_panel_data(const struct omap_dss_device *dssdev)
{
return (struct nokia_dsi_panel_data *) dssdev->data;
}
static void pnd_esd_work(struct work_struct *work);
static void hw_guard_start(struct pnd_data *td, int guard_msec)
{
td->hw_guard_wait = msecs_to_jiffies(guard_msec);
td->hw_guard_end = jiffies + td->hw_guard_wait;
}
static void hw_guard_wait(struct pnd_data *td)
{
unsigned long wait = td->hw_guard_end - jiffies;
if ((long)wait > 0 && wait <= td->hw_guard_wait) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(wait);
}
}
#define DISABLE_HS_MODE_FOR_READING_HACK
#ifndef DISABLE_HS_MODE_FOR_READING_HACK
static inline int pnd_dcs_read(u8 dcs_cmd, u8 *buf, int buflen)
{
return dsi_vc_dcs_read(TCH, dcs_cmd, buf, buflen);
}
#else
/*
* HACK: Workaround bug on some Pyrenees panels where reading fails if a read
* command is issued in HS mode. For now, always disable HS mode for reading,
* for all panels.
*/
static int pnd_dcs_read(u8 dcs_cmd, u8 *buf, int buflen)
{
bool hs;
int r;
hs = omapdss_dsi_vc_is_hs_enabled(TCH);
if (hs)
omapdss_dsi_vc_enable_hs(TCH, false);
r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, buflen);
if (hs)
omapdss_dsi_vc_enable_hs(TCH, true);
return r;
}
#endif
static int pnd_dcs_read_1(u8 dcs_cmd, u8 *data)
{
int r;
u8 buf[1];
r = pnd_dcs_read(dcs_cmd, buf, 1);
if (r < 0)
return r;
*data = buf[0];
return 0;
}
static inline int pnd_dcs_write_nosync_0(u8 dcs_cmd)
{
return dsi_vc_dcs_write_nosync(TCH, &dcs_cmd, 1);
}
static inline int pnd_dcs_write_nosync_1(u8 dcs_cmd, u8 param)
{
u8 buf[2];
buf[0] = dcs_cmd;
buf[1] = param;
return dsi_vc_dcs_write_nosync(TCH, buf, 2);
}
static int pnd_dcs_write_0(u8 dcs_cmd)
{
return dsi_vc_dcs_write(TCH, &dcs_cmd, 1);
}
static int pnd_dcs_write_1(u8 dcs_cmd, u8 param)
{
u8 buf[2];
buf[0] = dcs_cmd;
buf[1] = param;
return dsi_vc_dcs_write(TCH, buf, 2);
}
static int pnd_dcs_write_2(u8 dcs_cmd, u8 param1, u8 param2)
{
u8 buf[3];
buf[0] = dcs_cmd;
buf[1] = param1;
buf[2] = param2;
return dsi_vc_dcs_write(TCH, buf, 3);
}
static int pnd_sleep_in(struct pnd_data *td)
{
u8 cmd;
int r;
hw_guard_wait(td);
cmd = DCS_SLEEP_IN;
r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1);
if (r)
return r;
hw_guard_start(td, 120);
if (td->panel_config->sleep.sleep_in)
msleep(td->panel_config->sleep.sleep_in);
return 0;
}
static int pnd_sleep_out(struct pnd_data *td)
{
int r;
hw_guard_wait(td);
r = pnd_dcs_write_0(DCS_SLEEP_OUT);
if (r)
return r;
hw_guard_start(td, 120);
if (td->panel_config->sleep.sleep_out)
msleep(td->panel_config->sleep.sleep_out);
return 0;
}
static int pnd_get_id(u8 *id1, u8 *id2, u8 *id3)
{
int r;
r = pnd_dcs_read_1(DCS_GET_ID1, id1);
if (r)
return r;
r = pnd_dcs_read_1(DCS_GET_ID2, id2);
if (r)
return r;
r = pnd_dcs_read_1(DCS_GET_ID3, id3);
if (r)
return r;
return 0;
}
static int pnd_set_addr_mode(struct omap_dss_device *dssdev, u8 rotate,
bool mirror)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
int r;
u8 mode;
int b5, b6, b7;
r = pnd_dcs_read_1(DCS_READ_MADCTL, &mode);
if (r)
return r;
/* HACK: pyrenees TS1.1 support */
if (td->id1 == 0xfe && td->id2 == 0x80 && td->id3 == 0x96)
mode |= 9;
switch (rotate) {
default:
case 0:
b7 = 0;
b6 = 0;
b5 = 0;
break;
case 1:
b7 = 0;
b6 = 1;
b5 = 1;
break;
case 2:
b7 = 1;
b6 = 1;
b5 = 0;
break;
case 3:
b7 = 1;
b6 = 0;
b5 = 1;
break;
}
if (mirror)
b6 = !b6;
mode &= ~((1<<7) | (1<<6) | (1<<5));
mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
return pnd_dcs_write_1(DCS_MEM_ACC_CTRL, mode);
}
static int pnd_set_update_window(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int r;
u16 x1, x2, y1, y2;
u8 buf[5];
if (panel_data->partial_area.height) {
if (td->rotate == 0 || td->rotate == 2)
y += panel_data->partial_area.offset;
else
x += panel_data->partial_area.offset;
}
x1 = x;
x2 = x + w - 1;
y1 = y;
y2 = y + h - 1;
buf[0] = DCS_COLUMN_ADDR;
buf[1] = (x1 >> 8) & 0xff;
buf[2] = (x1 >> 0) & 0xff;
buf[3] = (x2 >> 8) & 0xff;
buf[4] = (x2 >> 0) & 0xff;
r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
if (r)
return r;
buf[0] = DCS_PAGE_ADDR;
buf[1] = (y1 >> 8) & 0xff;
buf[2] = (y1 >> 0) & 0xff;
buf[3] = (y2 >> 8) & 0xff;
buf[4] = (y2 >> 0) & 0xff;
r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
if (r)
return r;
return 0;
}
static int pnd_set_update_window_sync(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
{
int r;
r = pnd_set_update_window(dssdev, x, y, w, h);
if (r)
return r;
return dsi_vc_send_bta_sync(TCH);
}
static int pnd_set_partial_area(u16 start_row, u16 height)
{
u16 end_row = start_row + height - 1;
u8 partial_area[] = {
DCS_PARTIAL_AREA,
(start_row >> 8) & 0xff, start_row & 0xff,
(end_row >> 8) & 0xff, end_row & 0xff,
};
return dsi_vc_dcs_write(TCH, partial_area, sizeof(partial_area));
}
#define CALIBRATE_DELAY 5000
#define CALIBRATE_RETRY_DELAY 500
#define CALIBRATE_POWER_ON_DELAY 0
static void pnd_queue_calibration_work(struct omap_dss_device *dssdev,
unsigned long delay)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
if (panel_data->use_ext_te && !td->lpm_enabled &&
!td->reverse_te_irq_and_timer) {
dev_dbg(&dssdev->dev, "calibration: start in %lu ms\n", delay);
queue_delayed_work(td->wq, &td->calibration_work,
msecs_to_jiffies(delay));
}
}
static void pnd_cancel_calibration_work(struct omap_dss_device *dssdev)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
if (panel_data->use_ext_te)
cancel_delayed_work(&td->calibration_work);
}
static void pnd_queue_esd_work(struct omap_dss_device *dssdev)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
if (td->esd_timeout > 0 && td->lpm_enabled == false)
queue_delayed_work(td->wq, &td->esd_work,
msecs_to_jiffies(td->esd_timeout));
}
static void pnd_cancel_esd_work(struct omap_dss_device *dssdev)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
cancel_delayed_work(&td->esd_work);
}
static void pnd_queue_ulps_work(struct omap_dss_device *dssdev)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
if (td->ulps_timeout > 0)
queue_delayed_work(td->wq, &td->ulps_work,
msecs_to_jiffies(td->ulps_timeout));
}
static void pnd_cancel_ulps_work(struct omap_dss_device *dssdev)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
cancel_delayed_work(&td->ulps_work);
}
static int pnd_enter_ulps(struct omap_dss_device *dssdev)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int r;
if (td->ulps_enabled)
return 0;
pnd_cancel_ulps_work(dssdev);
r = _pnd_enable_te(dssdev, false);
if (r)
goto err;
disable_irq(gpio_to_irq(panel_data->ext_te_gpio));
omapdss_dsi_display_disable(dssdev, false, true);
td->ulps_enabled = true;
return 0;
err:
dev_err(&dssdev->dev, "enter ULPS failed");
pnd_panel_reset(dssdev);
td->ulps_enabled = false;
pnd_queue_ulps_work(dssdev);
return r;
}
static int pnd_exit_ulps(struct omap_dss_device *dssdev)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int te_irq_count;
int r;
if (!td->ulps_enabled)
return 0;
r = omapdss_dsi_display_enable(dssdev);
if (r)
goto err;
omapdss_dsi_vc_enable_hs(TCH, true);
r = _pnd_enable_te(dssdev, true);
if (r)
goto err;
te_irq_count = atomic_read(&td->te_irq_count);
enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
/*
* HACK: Ignore any TE irqs that occur upon enable irq, as they are
* bound to be incorrectly timed. The irq handler will skip the first
* two irqs, but if enable irq does *not* cause an irq, increment the
* irq count, in order to ignore as few irqs as possible.
*/
atomic_cmpxchg(&td->te_irq_count, te_irq_count, te_irq_count + 1);
pnd_queue_ulps_work(dssdev);
td->ulps_enabled = false;
return 0;
err:
dev_err(&dssdev->dev, "exit ULPS failed");
r = pnd_panel_reset(dssdev);
enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
td->ulps_enabled = false;
pnd_queue_ulps_work(dssdev);
return r;
}
static int pnd_wake_up(struct omap_dss_device *dssdev)
{
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
if (td->ulps_enabled)
return pnd_exit_ulps(dssdev);
pnd_cancel_ulps_work(dssdev);
pnd_queue_ulps_work(dssdev);
return 0;
}
static int _pnd_enable_lpm(struct omap_dss_device *dssdev, bool enable)
{
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int r;
if (!panel_data->partial_area.height) {
if (enable)
r = pnd_dcs_write_0(DCS_PARTIAL_MODE_ON);
else
r = pnd_dcs_write_0(DCS_NORMAL_MODE_ON);
if (r)
return r;
}
if (enable)
r = pnd_dcs_write_0(DCS_IDLE_MODE_ON);
else
r = pnd_dcs_write_0(DCS_IDLE_MODE_OFF);
return r;
}
static int pnd_bl_update_status(struct backlight_device *dev)
{
struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
struct pnd_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int r;
int level;
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
dev->props.power == FB_BLANK_UNBLANK)
level = dev->props.brightness;
else
level = 0;
dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
down(&td->lock);
if (td->use_dsi_bl) {
if (td->enabled) {
dsi_bus_lock();
r = pnd_wake_up(dssdev);
if (!r)
r = pnd_dcs_write_1(DCS_BRIGHTNESS, level);
dsi_bus_unlock();
} else {
r = 0;
}
} else {
if (!panel_data->set_backlight)
r = -EINVAL;
else
r = panel_data->set_backlight(dssdev, level);
}
up(&td->lock);
return r;
}
static int pnd_bl_get_intensity(struct backlight_device *dev)
{
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
dev->props.power == FB_BLANK_UNBLANK)
return dev->props.brightness;
return 0;
}
static struct backlight_ops pnd_bl_ops = {
.get_brightness = pnd_bl_get_intensity,
.update_status = pnd_bl_update_status,
};