diff options
author | Heiko Stübner <heiko@sntech.de> | 2012-04-28 06:21:12 -0400 |
---|---|---|
committer | Florian Tobias Schandinat <FlorianSchandinat@gmx.de> | 2012-04-29 15:35:40 -0400 |
commit | 96b1d500e0284136a524180a50a230e1df21252d (patch) | |
tree | 94824682c10aa9a354328ad5dd4eb9269016aed0 /drivers/video/auo_k1900fb.c | |
parent | 2c8304d3125b9c75797a35037945df63869bfdf6 (diff) |
video: auo_k190x: add driver for AUO-K1900 variant
This controller only supports smaller resolutions and only serial
updates, i.e. it has to wait for an update to finish before
starting another one.
Signed-off-by: Heiko Stübner <heiko@sntech.de>
Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Diffstat (limited to 'drivers/video/auo_k1900fb.c')
-rw-r--r-- | drivers/video/auo_k1900fb.c | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/drivers/video/auo_k1900fb.c b/drivers/video/auo_k1900fb.c new file mode 100644 index 000000000000..c36cf961dcb2 --- /dev/null +++ b/drivers/video/auo_k1900fb.c | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | * auok190xfb.c -- FB driver for AUO-K1900 controllers | ||
3 | * | ||
4 | * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de> | ||
5 | * | ||
6 | * based on broadsheetfb.c | ||
7 | * | ||
8 | * Copyright (C) 2008, Jaya Kumar | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. | ||
15 | * | ||
16 | * This driver is written to be used with the AUO-K1900 display controller. | ||
17 | * | ||
18 | * It is intended to be architecture independent. A board specific driver | ||
19 | * must be used to perform all the physical IO interactions. | ||
20 | * | ||
21 | * The controller supports different update modes: | ||
22 | * mode0+1 16 step gray (4bit) | ||
23 | * mode2 4 step gray (2bit) - FIXME: add strange refresh | ||
24 | * mode3 2 step gray (1bit) - FIXME: add strange refresh | ||
25 | * mode4 handwriting mode (strange behaviour) | ||
26 | * mode5 automatic selection of update mode | ||
27 | */ | ||
28 | |||
29 | #include <linux/module.h> | ||
30 | #include <linux/kernel.h> | ||
31 | #include <linux/errno.h> | ||
32 | #include <linux/string.h> | ||
33 | #include <linux/mm.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/delay.h> | ||
36 | #include <linux/interrupt.h> | ||
37 | #include <linux/fb.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <linux/platform_device.h> | ||
40 | #include <linux/list.h> | ||
41 | #include <linux/firmware.h> | ||
42 | #include <linux/gpio.h> | ||
43 | #include <linux/pm_runtime.h> | ||
44 | |||
45 | #include <video/auo_k190xfb.h> | ||
46 | |||
47 | #include "auo_k190x.h" | ||
48 | |||
49 | /* | ||
50 | * AUO-K1900 specific commands | ||
51 | */ | ||
52 | |||
53 | #define AUOK1900_CMD_PARTIALDISP 0x1001 | ||
54 | #define AUOK1900_CMD_ROTATION 0x1006 | ||
55 | #define AUOK1900_CMD_LUT_STOP 0x1009 | ||
56 | |||
57 | #define AUOK1900_INIT_TEMP_AVERAGE (1 << 13) | ||
58 | #define AUOK1900_INIT_ROTATE(_x) ((_x & 0x3) << 10) | ||
59 | #define AUOK1900_INIT_RESOLUTION(_res) ((_res & 0x7) << 2) | ||
60 | |||
61 | static void auok1900_init(struct auok190xfb_par *par) | ||
62 | { | ||
63 | struct auok190x_board *board = par->board; | ||
64 | u16 init_param = 0; | ||
65 | |||
66 | init_param |= AUOK1900_INIT_TEMP_AVERAGE; | ||
67 | init_param |= AUOK1900_INIT_ROTATE(par->rotation); | ||
68 | init_param |= AUOK190X_INIT_INVERSE_WHITE; | ||
69 | init_param |= AUOK190X_INIT_FORMAT0; | ||
70 | init_param |= AUOK1900_INIT_RESOLUTION(par->resolution); | ||
71 | init_param |= AUOK190X_INIT_SHIFT_RIGHT; | ||
72 | |||
73 | auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param); | ||
74 | |||
75 | /* let the controller finish */ | ||
76 | board->wait_for_rdy(par); | ||
77 | } | ||
78 | |||
79 | static void auok1900_update_region(struct auok190xfb_par *par, int mode, | ||
80 | u16 y1, u16 y2) | ||
81 | { | ||
82 | struct device *dev = par->info->device; | ||
83 | unsigned char *buf = (unsigned char *)par->info->screen_base; | ||
84 | int xres = par->info->var.xres; | ||
85 | u16 args[4]; | ||
86 | |||
87 | pm_runtime_get_sync(dev); | ||
88 | |||
89 | mutex_lock(&(par->io_lock)); | ||
90 | |||
91 | /* y1 and y2 must be a multiple of 2 so drop the lowest bit */ | ||
92 | y1 &= 0xfffe; | ||
93 | y2 &= 0xfffe; | ||
94 | |||
95 | dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n", | ||
96 | 1, y1+1, xres, y2-y1, mode); | ||
97 | |||
98 | /* to FIX handle different partial update modes */ | ||
99 | args[0] = mode | 1; | ||
100 | args[1] = y1 + 1; | ||
101 | args[2] = xres; | ||
102 | args[3] = y2 - y1; | ||
103 | buf += y1 * xres; | ||
104 | auok190x_send_cmdargs_pixels(par, AUOK1900_CMD_PARTIALDISP, 4, args, | ||
105 | ((y2 - y1) * xres)/2, (u16 *) buf); | ||
106 | auok190x_send_command(par, AUOK190X_CMD_DATA_STOP); | ||
107 | |||
108 | par->update_cnt++; | ||
109 | |||
110 | mutex_unlock(&(par->io_lock)); | ||
111 | |||
112 | pm_runtime_mark_last_busy(dev); | ||
113 | pm_runtime_put_autosuspend(dev); | ||
114 | } | ||
115 | |||
116 | static void auok1900fb_dpy_update_pages(struct auok190xfb_par *par, | ||
117 | u16 y1, u16 y2) | ||
118 | { | ||
119 | int mode; | ||
120 | |||
121 | if (par->update_mode < 0) { | ||
122 | mode = AUOK190X_UPDATE_MODE(1); | ||
123 | par->last_mode = -1; | ||
124 | } else { | ||
125 | mode = AUOK190X_UPDATE_MODE(par->update_mode); | ||
126 | par->last_mode = par->update_mode; | ||
127 | } | ||
128 | |||
129 | if (par->flash) | ||
130 | mode |= AUOK190X_UPDATE_NONFLASH; | ||
131 | |||
132 | auok1900_update_region(par, mode, y1, y2); | ||
133 | } | ||
134 | |||
135 | static void auok1900fb_dpy_update(struct auok190xfb_par *par) | ||
136 | { | ||
137 | int mode; | ||
138 | |||
139 | if (par->update_mode < 0) { | ||
140 | mode = AUOK190X_UPDATE_MODE(0); | ||
141 | par->last_mode = -1; | ||
142 | } else { | ||
143 | mode = AUOK190X_UPDATE_MODE(par->update_mode); | ||
144 | par->last_mode = par->update_mode; | ||
145 | } | ||
146 | |||
147 | if (par->flash) | ||
148 | mode |= AUOK190X_UPDATE_NONFLASH; | ||
149 | |||
150 | auok1900_update_region(par, mode, 0, par->info->var.yres); | ||
151 | par->update_cnt = 0; | ||
152 | } | ||
153 | |||
154 | static bool auok1900fb_need_refresh(struct auok190xfb_par *par) | ||
155 | { | ||
156 | return (par->update_cnt > 10); | ||
157 | } | ||
158 | |||
159 | static int __devinit auok1900fb_probe(struct platform_device *pdev) | ||
160 | { | ||
161 | struct auok190x_init_data init; | ||
162 | struct auok190x_board *board; | ||
163 | |||
164 | /* pick up board specific routines */ | ||
165 | board = pdev->dev.platform_data; | ||
166 | if (!board) | ||
167 | return -EINVAL; | ||
168 | |||
169 | /* fill temporary init struct for common init */ | ||
170 | init.id = "auo_k1900fb"; | ||
171 | init.board = board; | ||
172 | init.update_partial = auok1900fb_dpy_update_pages; | ||
173 | init.update_all = auok1900fb_dpy_update; | ||
174 | init.need_refresh = auok1900fb_need_refresh; | ||
175 | init.init = auok1900_init; | ||
176 | |||
177 | return auok190x_common_probe(pdev, &init); | ||
178 | } | ||
179 | |||
180 | static int __devexit auok1900fb_remove(struct platform_device *pdev) | ||
181 | { | ||
182 | return auok190x_common_remove(pdev); | ||
183 | } | ||
184 | |||
185 | static struct platform_driver auok1900fb_driver = { | ||
186 | .probe = auok1900fb_probe, | ||
187 | .remove = __devexit_p(auok1900fb_remove), | ||
188 | .driver = { | ||
189 | .owner = THIS_MODULE, | ||
190 | .name = "auo_k1900fb", | ||
191 | .pm = &auok190x_pm, | ||
192 | }, | ||
193 | }; | ||
194 | module_platform_driver(auok1900fb_driver); | ||
195 | |||
196 | MODULE_DESCRIPTION("framebuffer driver for the AUO-K1900 EPD controller"); | ||
197 | MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); | ||
198 | MODULE_LICENSE("GPL"); | ||