diff options
author | Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> | 2013-06-14 05:52:18 -0400 |
---|---|---|
committer | Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> | 2013-06-14 05:52:18 -0400 |
commit | 8ea2c86449bdf0fe32fd043c714054962d8802ca (patch) | |
tree | 494b62a20f8e3203a7e1a55be3d68f41eae29306 | |
parent | 317ddd256b9c24b0d78fa8018f80f1e495481a10 (diff) | |
parent | ffa3fd21de8ab0db7962b612d4c6e17c0d88e9c2 (diff) |
Merge branch 'fbdev-3.11' of git://gitorious.org/linux-omap-dss2/linux into fbdev/for-next
-rw-r--r-- | Documentation/devicetree/bindings/video/ssd1307fb.txt | 10 | ||||
-rw-r--r-- | drivers/video/of_display_timing.c | 55 | ||||
-rw-r--r-- | drivers/video/ssd1307fb.c | 392 | ||||
-rw-r--r-- | include/video/of_display_timing.h | 2 |
4 files changed, 339 insertions, 120 deletions
diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt index 3d0060cff062..7a125427ff4b 100644 --- a/Documentation/devicetree/bindings/video/ssd1307fb.txt +++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt | |||
@@ -1,13 +1,17 @@ | |||
1 | * Solomon SSD1307 Framebuffer Driver | 1 | * Solomon SSD1307 Framebuffer Driver |
2 | 2 | ||
3 | Required properties: | 3 | Required properties: |
4 | - compatible: Should be "solomon,ssd1307fb-<bus>". The only supported bus for | 4 | - compatible: Should be "solomon,<chip>fb-<bus>". The only supported bus for |
5 | now is i2c. | 5 | now is i2c, and the supported chips are ssd1306 and ssd1307. |
6 | - reg: Should contain address of the controller on the I2C bus. Most likely | 6 | - reg: Should contain address of the controller on the I2C bus. Most likely |
7 | 0x3c or 0x3d | 7 | 0x3c or 0x3d |
8 | - pwm: Should contain the pwm to use according to the OF device tree PWM | 8 | - pwm: Should contain the pwm to use according to the OF device tree PWM |
9 | specification [0] | 9 | specification [0]. Only required for the ssd1307. |
10 | - reset-gpios: Should contain the GPIO used to reset the OLED display | 10 | - reset-gpios: Should contain the GPIO used to reset the OLED display |
11 | - solomon,height: Height in pixel of the screen driven by the controller | ||
12 | - solomon,width: Width in pixel of the screen driven by the controller | ||
13 | - solomon,page-offset: Offset of pages (band of 8 pixels) that the screen is | ||
14 | mapped to. | ||
11 | 15 | ||
12 | Optional properties: | 16 | Optional properties: |
13 | - reset-active-low: Is the reset gpio is active on physical low? | 17 | - reset-active-low: Is the reset gpio is active on physical low? |
diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c index 56009bc02b02..9c0f17b2e6fb 100644 --- a/drivers/video/of_display_timing.c +++ b/drivers/video/of_display_timing.c | |||
@@ -53,21 +53,16 @@ static int parse_timing_property(struct device_node *np, const char *name, | |||
53 | } | 53 | } |
54 | 54 | ||
55 | /** | 55 | /** |
56 | * of_get_display_timing - parse display_timing entry from device_node | 56 | * of_parse_display_timing - parse display_timing entry from device_node |
57 | * @np: device_node with the properties | 57 | * @np: device_node with the properties |
58 | **/ | 58 | **/ |
59 | static struct display_timing *of_get_display_timing(struct device_node *np) | 59 | static int of_parse_display_timing(struct device_node *np, |
60 | struct display_timing *dt) | ||
60 | { | 61 | { |
61 | struct display_timing *dt; | ||
62 | u32 val = 0; | 62 | u32 val = 0; |
63 | int ret = 0; | 63 | int ret = 0; |
64 | 64 | ||
65 | dt = kzalloc(sizeof(*dt), GFP_KERNEL); | 65 | memset(dt, 0, sizeof(*dt)); |
66 | if (!dt) { | ||
67 | pr_err("%s: could not allocate display_timing struct\n", | ||
68 | of_node_full_name(np)); | ||
69 | return NULL; | ||
70 | } | ||
71 | 66 | ||
72 | ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch); | 67 | ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch); |
73 | ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch); | 68 | ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch); |
@@ -101,12 +96,38 @@ static struct display_timing *of_get_display_timing(struct device_node *np) | |||
101 | if (ret) { | 96 | if (ret) { |
102 | pr_err("%s: error reading timing properties\n", | 97 | pr_err("%s: error reading timing properties\n", |
103 | of_node_full_name(np)); | 98 | of_node_full_name(np)); |
104 | kfree(dt); | 99 | return -EINVAL; |
105 | return NULL; | 100 | } |
101 | |||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * of_get_display_timing - parse a display_timing entry | ||
107 | * @np: device_node with the timing subnode | ||
108 | * @name: name of the timing node | ||
109 | * @dt: display_timing struct to fill | ||
110 | **/ | ||
111 | int of_get_display_timing(struct device_node *np, const char *name, | ||
112 | struct display_timing *dt) | ||
113 | { | ||
114 | struct device_node *timing_np; | ||
115 | |||
116 | if (!np) { | ||
117 | pr_err("%s: no devicenode given\n", of_node_full_name(np)); | ||
118 | return -EINVAL; | ||
106 | } | 119 | } |
107 | 120 | ||
108 | return dt; | 121 | timing_np = of_find_node_by_name(np, name); |
122 | if (!timing_np) { | ||
123 | pr_err("%s: could not find node '%s'\n", | ||
124 | of_node_full_name(np), name); | ||
125 | return -ENOENT; | ||
126 | } | ||
127 | |||
128 | return of_parse_display_timing(timing_np, dt); | ||
109 | } | 129 | } |
130 | EXPORT_SYMBOL_GPL(of_get_display_timing); | ||
110 | 131 | ||
111 | /** | 132 | /** |
112 | * of_get_display_timings - parse all display_timing entries from a device_node | 133 | * of_get_display_timings - parse all display_timing entries from a device_node |
@@ -174,9 +195,17 @@ struct display_timings *of_get_display_timings(struct device_node *np) | |||
174 | 195 | ||
175 | for_each_child_of_node(timings_np, entry) { | 196 | for_each_child_of_node(timings_np, entry) { |
176 | struct display_timing *dt; | 197 | struct display_timing *dt; |
198 | int r; | ||
177 | 199 | ||
178 | dt = of_get_display_timing(entry); | 200 | dt = kzalloc(sizeof(*dt), GFP_KERNEL); |
179 | if (!dt) { | 201 | if (!dt) { |
202 | pr_err("%s: could not allocate display_timing struct\n", | ||
203 | of_node_full_name(np)); | ||
204 | goto timingfail; | ||
205 | } | ||
206 | |||
207 | r = of_parse_display_timing(entry, dt); | ||
208 | if (r) { | ||
180 | /* | 209 | /* |
181 | * to not encourage wrong devicetrees, fail in case of | 210 | * to not encourage wrong devicetrees, fail in case of |
182 | * an error | 211 | * an error |
diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c index 9ef05d3ef68a..44967c8fef2b 100644 --- a/drivers/video/ssd1307fb.c +++ b/drivers/video/ssd1307fb.c | |||
@@ -16,24 +16,50 @@ | |||
16 | #include <linux/pwm.h> | 16 | #include <linux/pwm.h> |
17 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
18 | 18 | ||
19 | #define SSD1307FB_WIDTH 96 | ||
20 | #define SSD1307FB_HEIGHT 16 | ||
21 | |||
22 | #define SSD1307FB_DATA 0x40 | 19 | #define SSD1307FB_DATA 0x40 |
23 | #define SSD1307FB_COMMAND 0x80 | 20 | #define SSD1307FB_COMMAND 0x80 |
24 | 21 | ||
22 | #define SSD1307FB_SET_ADDRESS_MODE 0x20 | ||
23 | #define SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL (0x00) | ||
24 | #define SSD1307FB_SET_ADDRESS_MODE_VERTICAL (0x01) | ||
25 | #define SSD1307FB_SET_ADDRESS_MODE_PAGE (0x02) | ||
26 | #define SSD1307FB_SET_COL_RANGE 0x21 | ||
27 | #define SSD1307FB_SET_PAGE_RANGE 0x22 | ||
25 | #define SSD1307FB_CONTRAST 0x81 | 28 | #define SSD1307FB_CONTRAST 0x81 |
29 | #define SSD1307FB_CHARGE_PUMP 0x8d | ||
26 | #define SSD1307FB_SEG_REMAP_ON 0xa1 | 30 | #define SSD1307FB_SEG_REMAP_ON 0xa1 |
27 | #define SSD1307FB_DISPLAY_OFF 0xae | 31 | #define SSD1307FB_DISPLAY_OFF 0xae |
32 | #define SSD1307FB_SET_MULTIPLEX_RATIO 0xa8 | ||
28 | #define SSD1307FB_DISPLAY_ON 0xaf | 33 | #define SSD1307FB_DISPLAY_ON 0xaf |
29 | #define SSD1307FB_START_PAGE_ADDRESS 0xb0 | 34 | #define SSD1307FB_START_PAGE_ADDRESS 0xb0 |
35 | #define SSD1307FB_SET_DISPLAY_OFFSET 0xd3 | ||
36 | #define SSD1307FB_SET_CLOCK_FREQ 0xd5 | ||
37 | #define SSD1307FB_SET_PRECHARGE_PERIOD 0xd9 | ||
38 | #define SSD1307FB_SET_COM_PINS_CONFIG 0xda | ||
39 | #define SSD1307FB_SET_VCOMH 0xdb | ||
40 | |||
41 | struct ssd1307fb_par; | ||
42 | |||
43 | struct ssd1307fb_ops { | ||
44 | int (*init)(struct ssd1307fb_par *); | ||
45 | int (*remove)(struct ssd1307fb_par *); | ||
46 | }; | ||
30 | 47 | ||
31 | struct ssd1307fb_par { | 48 | struct ssd1307fb_par { |
32 | struct i2c_client *client; | 49 | struct i2c_client *client; |
50 | u32 height; | ||
33 | struct fb_info *info; | 51 | struct fb_info *info; |
52 | struct ssd1307fb_ops *ops; | ||
53 | u32 page_offset; | ||
34 | struct pwm_device *pwm; | 54 | struct pwm_device *pwm; |
35 | u32 pwm_period; | 55 | u32 pwm_period; |
36 | int reset; | 56 | int reset; |
57 | u32 width; | ||
58 | }; | ||
59 | |||
60 | struct ssd1307fb_array { | ||
61 | u8 type; | ||
62 | u8 data[0]; | ||
37 | }; | 63 | }; |
38 | 64 | ||
39 | static struct fb_fix_screeninfo ssd1307fb_fix = { | 65 | static struct fb_fix_screeninfo ssd1307fb_fix = { |
@@ -43,68 +69,87 @@ static struct fb_fix_screeninfo ssd1307fb_fix = { | |||
43 | .xpanstep = 0, | 69 | .xpanstep = 0, |
44 | .ypanstep = 0, | 70 | .ypanstep = 0, |
45 | .ywrapstep = 0, | 71 | .ywrapstep = 0, |
46 | .line_length = SSD1307FB_WIDTH / 8, | ||
47 | .accel = FB_ACCEL_NONE, | 72 | .accel = FB_ACCEL_NONE, |
48 | }; | 73 | }; |
49 | 74 | ||
50 | static struct fb_var_screeninfo ssd1307fb_var = { | 75 | static struct fb_var_screeninfo ssd1307fb_var = { |
51 | .xres = SSD1307FB_WIDTH, | ||
52 | .yres = SSD1307FB_HEIGHT, | ||
53 | .xres_virtual = SSD1307FB_WIDTH, | ||
54 | .yres_virtual = SSD1307FB_HEIGHT, | ||
55 | .bits_per_pixel = 1, | 76 | .bits_per_pixel = 1, |
56 | }; | 77 | }; |
57 | 78 | ||
58 | static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8 *cmd, u32 len) | 79 | static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type) |
59 | { | 80 | { |
60 | u8 *buf; | 81 | struct ssd1307fb_array *array; |
61 | int ret = 0; | ||
62 | |||
63 | buf = kzalloc(len + 1, GFP_KERNEL); | ||
64 | if (!buf) { | ||
65 | dev_err(&client->dev, "Couldn't allocate sending buffer.\n"); | ||
66 | return -ENOMEM; | ||
67 | } | ||
68 | 82 | ||
69 | buf[0] = type; | 83 | array = kzalloc(sizeof(struct ssd1307fb_array) + len, GFP_KERNEL); |
70 | memcpy(buf + 1, cmd, len); | 84 | if (!array) |
85 | return NULL; | ||
71 | 86 | ||
72 | ret = i2c_master_send(client, buf, len + 1); | 87 | array->type = type; |
73 | if (ret != len + 1) { | ||
74 | dev_err(&client->dev, "Couldn't send I2C command.\n"); | ||
75 | goto error; | ||
76 | } | ||
77 | 88 | ||
78 | error: | 89 | return array; |
79 | kfree(buf); | ||
80 | return ret; | ||
81 | } | 90 | } |
82 | 91 | ||
83 | static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8 *cmd, u32 len) | 92 | static int ssd1307fb_write_array(struct i2c_client *client, |
93 | struct ssd1307fb_array *array, u32 len) | ||
84 | { | 94 | { |
85 | return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len); | 95 | int ret; |
96 | |||
97 | len += sizeof(struct ssd1307fb_array); | ||
98 | |||
99 | ret = i2c_master_send(client, (u8 *)array, len); | ||
100 | if (ret != len) { | ||
101 | dev_err(&client->dev, "Couldn't send I2C command.\n"); | ||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | return 0; | ||
86 | } | 106 | } |
87 | 107 | ||
88 | static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) | 108 | static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) |
89 | { | 109 | { |
90 | return ssd1307fb_write_cmd_array(client, &cmd, 1); | 110 | struct ssd1307fb_array *array; |
91 | } | 111 | int ret; |
92 | 112 | ||
93 | static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8 *cmd, u32 len) | 113 | array = ssd1307fb_alloc_array(1, SSD1307FB_COMMAND); |
94 | { | 114 | if (!array) |
95 | return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len); | 115 | return -ENOMEM; |
116 | |||
117 | array->data[0] = cmd; | ||
118 | |||
119 | ret = ssd1307fb_write_array(client, array, 1); | ||
120 | kfree(array); | ||
121 | |||
122 | return ret; | ||
96 | } | 123 | } |
97 | 124 | ||
98 | static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data) | 125 | static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data) |
99 | { | 126 | { |
100 | return ssd1307fb_write_data_array(client, &data, 1); | 127 | struct ssd1307fb_array *array; |
128 | int ret; | ||
129 | |||
130 | array = ssd1307fb_alloc_array(1, SSD1307FB_DATA); | ||
131 | if (!array) | ||
132 | return -ENOMEM; | ||
133 | |||
134 | array->data[0] = data; | ||
135 | |||
136 | ret = ssd1307fb_write_array(client, array, 1); | ||
137 | kfree(array); | ||
138 | |||
139 | return ret; | ||
101 | } | 140 | } |
102 | 141 | ||
103 | static void ssd1307fb_update_display(struct ssd1307fb_par *par) | 142 | static void ssd1307fb_update_display(struct ssd1307fb_par *par) |
104 | { | 143 | { |
144 | struct ssd1307fb_array *array; | ||
105 | u8 *vmem = par->info->screen_base; | 145 | u8 *vmem = par->info->screen_base; |
106 | int i, j, k; | 146 | int i, j, k; |
107 | 147 | ||
148 | array = ssd1307fb_alloc_array(par->width * par->height / 8, | ||
149 | SSD1307FB_DATA); | ||
150 | if (!array) | ||
151 | return; | ||
152 | |||
108 | /* | 153 | /* |
109 | * The screen is divided in pages, each having a height of 8 | 154 | * The screen is divided in pages, each having a height of 8 |
110 | * pixels, and the width of the screen. When sending a byte of | 155 | * pixels, and the width of the screen. When sending a byte of |
@@ -134,24 +179,23 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par) | |||
134 | * (5) A4 B4 C4 D4 E4 F4 G4 H4 | 179 | * (5) A4 B4 C4 D4 E4 F4 G4 H4 |
135 | */ | 180 | */ |
136 | 181 | ||
137 | for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) { | 182 | for (i = 0; i < (par->height / 8); i++) { |
138 | ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1)); | 183 | for (j = 0; j < par->width; j++) { |
139 | ssd1307fb_write_cmd(par->client, 0x00); | 184 | u32 array_idx = i * par->width + j; |
140 | ssd1307fb_write_cmd(par->client, 0x10); | 185 | array->data[array_idx] = 0; |
141 | |||
142 | for (j = 0; j < SSD1307FB_WIDTH; j++) { | ||
143 | u8 buf = 0; | ||
144 | for (k = 0; k < 8; k++) { | 186 | for (k = 0; k < 8; k++) { |
145 | u32 page_length = SSD1307FB_WIDTH * i; | 187 | u32 page_length = par->width * i; |
146 | u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8; | 188 | u32 index = page_length + (par->width * k + j) / 8; |
147 | u8 byte = *(vmem + index); | 189 | u8 byte = *(vmem + index); |
148 | u8 bit = byte & (1 << (j % 8)); | 190 | u8 bit = byte & (1 << (j % 8)); |
149 | bit = bit >> (j % 8); | 191 | bit = bit >> (j % 8); |
150 | buf |= bit << k; | 192 | array->data[array_idx] |= bit << k; |
151 | } | 193 | } |
152 | ssd1307fb_write_data(par->client, buf); | ||
153 | } | 194 | } |
154 | } | 195 | } |
196 | |||
197 | ssd1307fb_write_array(par->client, array, par->width * par->height / 8); | ||
198 | kfree(array); | ||
155 | } | 199 | } |
156 | 200 | ||
157 | 201 | ||
@@ -227,16 +271,167 @@ static struct fb_deferred_io ssd1307fb_defio = { | |||
227 | .deferred_io = ssd1307fb_deferred_io, | 271 | .deferred_io = ssd1307fb_deferred_io, |
228 | }; | 272 | }; |
229 | 273 | ||
274 | static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par) | ||
275 | { | ||
276 | int ret; | ||
277 | |||
278 | par->pwm = pwm_get(&par->client->dev, NULL); | ||
279 | if (IS_ERR(par->pwm)) { | ||
280 | dev_err(&par->client->dev, "Could not get PWM from device tree!\n"); | ||
281 | return PTR_ERR(par->pwm); | ||
282 | } | ||
283 | |||
284 | par->pwm_period = pwm_get_period(par->pwm); | ||
285 | /* Enable the PWM */ | ||
286 | pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); | ||
287 | pwm_enable(par->pwm); | ||
288 | |||
289 | dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n", | ||
290 | par->pwm->pwm, par->pwm_period); | ||
291 | |||
292 | /* Map column 127 of the OLED to segment 0 */ | ||
293 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON); | ||
294 | if (ret < 0) | ||
295 | return ret; | ||
296 | |||
297 | /* Turn on the display */ | ||
298 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); | ||
299 | if (ret < 0) | ||
300 | return ret; | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par) | ||
306 | { | ||
307 | pwm_disable(par->pwm); | ||
308 | pwm_put(par->pwm); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = { | ||
313 | .init = ssd1307fb_ssd1307_init, | ||
314 | .remove = ssd1307fb_ssd1307_remove, | ||
315 | }; | ||
316 | |||
317 | static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par) | ||
318 | { | ||
319 | int ret; | ||
320 | |||
321 | /* Set initial contrast */ | ||
322 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST); | ||
323 | ret = ret & ssd1307fb_write_cmd(par->client, 0x7f); | ||
324 | if (ret < 0) | ||
325 | return ret; | ||
326 | |||
327 | /* Set COM direction */ | ||
328 | ret = ssd1307fb_write_cmd(par->client, 0xc8); | ||
329 | if (ret < 0) | ||
330 | return ret; | ||
331 | |||
332 | /* Set segment re-map */ | ||
333 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON); | ||
334 | if (ret < 0) | ||
335 | return ret; | ||
336 | |||
337 | /* Set multiplex ratio value */ | ||
338 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO); | ||
339 | ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1); | ||
340 | if (ret < 0) | ||
341 | return ret; | ||
342 | |||
343 | /* set display offset value */ | ||
344 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET); | ||
345 | ret = ssd1307fb_write_cmd(par->client, 0x20); | ||
346 | if (ret < 0) | ||
347 | return ret; | ||
348 | |||
349 | /* Set clock frequency */ | ||
350 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ); | ||
351 | ret = ret & ssd1307fb_write_cmd(par->client, 0xf0); | ||
352 | if (ret < 0) | ||
353 | return ret; | ||
354 | |||
355 | /* Set precharge period in number of ticks from the internal clock */ | ||
356 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD); | ||
357 | ret = ret & ssd1307fb_write_cmd(par->client, 0x22); | ||
358 | if (ret < 0) | ||
359 | return ret; | ||
360 | |||
361 | /* Set COM pins configuration */ | ||
362 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG); | ||
363 | ret = ret & ssd1307fb_write_cmd(par->client, 0x22); | ||
364 | if (ret < 0) | ||
365 | return ret; | ||
366 | |||
367 | /* Set VCOMH */ | ||
368 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH); | ||
369 | ret = ret & ssd1307fb_write_cmd(par->client, 0x49); | ||
370 | if (ret < 0) | ||
371 | return ret; | ||
372 | |||
373 | /* Turn on the DC-DC Charge Pump */ | ||
374 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP); | ||
375 | ret = ret & ssd1307fb_write_cmd(par->client, 0x14); | ||
376 | if (ret < 0) | ||
377 | return ret; | ||
378 | |||
379 | /* Switch to horizontal addressing mode */ | ||
380 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE); | ||
381 | ret = ret & ssd1307fb_write_cmd(par->client, | ||
382 | SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); | ||
383 | if (ret < 0) | ||
384 | return ret; | ||
385 | |||
386 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE); | ||
387 | ret = ret & ssd1307fb_write_cmd(par->client, 0x0); | ||
388 | ret = ret & ssd1307fb_write_cmd(par->client, par->width - 1); | ||
389 | if (ret < 0) | ||
390 | return ret; | ||
391 | |||
392 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE); | ||
393 | ret = ret & ssd1307fb_write_cmd(par->client, 0x0); | ||
394 | ret = ret & ssd1307fb_write_cmd(par->client, | ||
395 | par->page_offset + (par->height / 8) - 1); | ||
396 | if (ret < 0) | ||
397 | return ret; | ||
398 | |||
399 | /* Turn on the display */ | ||
400 | ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); | ||
401 | if (ret < 0) | ||
402 | return ret; | ||
403 | |||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = { | ||
408 | .init = ssd1307fb_ssd1306_init, | ||
409 | }; | ||
410 | |||
411 | static const struct of_device_id ssd1307fb_of_match[] = { | ||
412 | { | ||
413 | .compatible = "solomon,ssd1306fb-i2c", | ||
414 | .data = (void *)&ssd1307fb_ssd1306_ops, | ||
415 | }, | ||
416 | { | ||
417 | .compatible = "solomon,ssd1307fb-i2c", | ||
418 | .data = (void *)&ssd1307fb_ssd1307_ops, | ||
419 | }, | ||
420 | {}, | ||
421 | }; | ||
422 | MODULE_DEVICE_TABLE(of, ssd1307fb_of_match); | ||
423 | |||
230 | static int ssd1307fb_probe(struct i2c_client *client, | 424 | static int ssd1307fb_probe(struct i2c_client *client, |
231 | const struct i2c_device_id *id) | 425 | const struct i2c_device_id *id) |
232 | { | 426 | { |
233 | struct fb_info *info; | 427 | struct fb_info *info; |
234 | u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8; | 428 | struct device_node *node = client->dev.of_node; |
429 | u32 vmem_size; | ||
235 | struct ssd1307fb_par *par; | 430 | struct ssd1307fb_par *par; |
236 | u8 *vmem; | 431 | u8 *vmem; |
237 | int ret; | 432 | int ret; |
238 | 433 | ||
239 | if (!client->dev.of_node) { | 434 | if (!node) { |
240 | dev_err(&client->dev, "No device tree data found!\n"); | 435 | dev_err(&client->dev, "No device tree data found!\n"); |
241 | return -EINVAL; | 436 | return -EINVAL; |
242 | } | 437 | } |
@@ -247,6 +442,31 @@ static int ssd1307fb_probe(struct i2c_client *client, | |||
247 | return -ENOMEM; | 442 | return -ENOMEM; |
248 | } | 443 | } |
249 | 444 | ||
445 | par = info->par; | ||
446 | par->info = info; | ||
447 | par->client = client; | ||
448 | |||
449 | par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match, | ||
450 | &client->dev)->data; | ||
451 | |||
452 | par->reset = of_get_named_gpio(client->dev.of_node, | ||
453 | "reset-gpios", 0); | ||
454 | if (!gpio_is_valid(par->reset)) { | ||
455 | ret = -EINVAL; | ||
456 | goto fb_alloc_error; | ||
457 | } | ||
458 | |||
459 | if (of_property_read_u32(node, "solomon,width", &par->width)) | ||
460 | par->width = 96; | ||
461 | |||
462 | if (of_property_read_u32(node, "solomon,height", &par->height)) | ||
463 | par->width = 16; | ||
464 | |||
465 | if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset)) | ||
466 | par->page_offset = 1; | ||
467 | |||
468 | vmem_size = par->width * par->height / 8; | ||
469 | |||
250 | vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL); | 470 | vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL); |
251 | if (!vmem) { | 471 | if (!vmem) { |
252 | dev_err(&client->dev, "Couldn't allocate graphical memory.\n"); | 472 | dev_err(&client->dev, "Couldn't allocate graphical memory.\n"); |
@@ -256,9 +476,15 @@ static int ssd1307fb_probe(struct i2c_client *client, | |||
256 | 476 | ||
257 | info->fbops = &ssd1307fb_ops; | 477 | info->fbops = &ssd1307fb_ops; |
258 | info->fix = ssd1307fb_fix; | 478 | info->fix = ssd1307fb_fix; |
479 | info->fix.line_length = par->width / 8; | ||
259 | info->fbdefio = &ssd1307fb_defio; | 480 | info->fbdefio = &ssd1307fb_defio; |
260 | 481 | ||
261 | info->var = ssd1307fb_var; | 482 | info->var = ssd1307fb_var; |
483 | info->var.xres = par->width; | ||
484 | info->var.xres_virtual = par->width; | ||
485 | info->var.yres = par->height; | ||
486 | info->var.yres_virtual = par->height; | ||
487 | |||
262 | info->var.red.length = 1; | 488 | info->var.red.length = 1; |
263 | info->var.red.offset = 0; | 489 | info->var.red.offset = 0; |
264 | info->var.green.length = 1; | 490 | info->var.green.length = 1; |
@@ -272,17 +498,6 @@ static int ssd1307fb_probe(struct i2c_client *client, | |||
272 | 498 | ||
273 | fb_deferred_io_init(info); | 499 | fb_deferred_io_init(info); |
274 | 500 | ||
275 | par = info->par; | ||
276 | par->info = info; | ||
277 | par->client = client; | ||
278 | |||
279 | par->reset = of_get_named_gpio(client->dev.of_node, | ||
280 | "reset-gpios", 0); | ||
281 | if (!gpio_is_valid(par->reset)) { | ||
282 | ret = -EINVAL; | ||
283 | goto reset_oled_error; | ||
284 | } | ||
285 | |||
286 | ret = devm_gpio_request_one(&client->dev, par->reset, | 501 | ret = devm_gpio_request_one(&client->dev, par->reset, |
287 | GPIOF_OUT_INIT_HIGH, | 502 | GPIOF_OUT_INIT_HIGH, |
288 | "oled-reset"); | 503 | "oled-reset"); |
@@ -293,23 +508,6 @@ static int ssd1307fb_probe(struct i2c_client *client, | |||
293 | goto reset_oled_error; | 508 | goto reset_oled_error; |
294 | } | 509 | } |
295 | 510 | ||
296 | par->pwm = pwm_get(&client->dev, NULL); | ||
297 | if (IS_ERR(par->pwm)) { | ||
298 | dev_err(&client->dev, "Could not get PWM from device tree!\n"); | ||
299 | ret = PTR_ERR(par->pwm); | ||
300 | goto pwm_error; | ||
301 | } | ||
302 | |||
303 | par->pwm_period = pwm_get_period(par->pwm); | ||
304 | |||
305 | dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period); | ||
306 | |||
307 | ret = register_framebuffer(info); | ||
308 | if (ret) { | ||
309 | dev_err(&client->dev, "Couldn't register the framebuffer\n"); | ||
310 | goto fbreg_error; | ||
311 | } | ||
312 | |||
313 | i2c_set_clientdata(client, info); | 511 | i2c_set_clientdata(client, info); |
314 | 512 | ||
315 | /* Reset the screen */ | 513 | /* Reset the screen */ |
@@ -318,34 +516,25 @@ static int ssd1307fb_probe(struct i2c_client *client, | |||
318 | gpio_set_value(par->reset, 1); | 516 | gpio_set_value(par->reset, 1); |
319 | udelay(4); | 517 | udelay(4); |
320 | 518 | ||
321 | /* Enable the PWM */ | 519 | if (par->ops->init) { |
322 | pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); | 520 | ret = par->ops->init(par); |
323 | pwm_enable(par->pwm); | 521 | if (ret) |
324 | 522 | goto reset_oled_error; | |
325 | /* Map column 127 of the OLED to segment 0 */ | ||
326 | ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON); | ||
327 | if (ret < 0) { | ||
328 | dev_err(&client->dev, "Couldn't remap the screen.\n"); | ||
329 | goto remap_error; | ||
330 | } | 523 | } |
331 | 524 | ||
332 | /* Turn on the display */ | 525 | ret = register_framebuffer(info); |
333 | ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON); | 526 | if (ret) { |
334 | if (ret < 0) { | 527 | dev_err(&client->dev, "Couldn't register the framebuffer\n"); |
335 | dev_err(&client->dev, "Couldn't turn the display on.\n"); | 528 | goto panel_init_error; |
336 | goto remap_error; | ||
337 | } | 529 | } |
338 | 530 | ||
339 | dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size); | 531 | dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size); |
340 | 532 | ||
341 | return 0; | 533 | return 0; |
342 | 534 | ||
343 | remap_error: | 535 | panel_init_error: |
344 | unregister_framebuffer(info); | 536 | if (par->ops->remove) |
345 | pwm_disable(par->pwm); | 537 | par->ops->remove(par); |
346 | fbreg_error: | ||
347 | pwm_put(par->pwm); | ||
348 | pwm_error: | ||
349 | reset_oled_error: | 538 | reset_oled_error: |
350 | fb_deferred_io_cleanup(info); | 539 | fb_deferred_io_cleanup(info); |
351 | fb_alloc_error: | 540 | fb_alloc_error: |
@@ -359,8 +548,8 @@ static int ssd1307fb_remove(struct i2c_client *client) | |||
359 | struct ssd1307fb_par *par = info->par; | 548 | struct ssd1307fb_par *par = info->par; |
360 | 549 | ||
361 | unregister_framebuffer(info); | 550 | unregister_framebuffer(info); |
362 | pwm_disable(par->pwm); | 551 | if (par->ops->remove) |
363 | pwm_put(par->pwm); | 552 | par->ops->remove(par); |
364 | fb_deferred_io_cleanup(info); | 553 | fb_deferred_io_cleanup(info); |
365 | framebuffer_release(info); | 554 | framebuffer_release(info); |
366 | 555 | ||
@@ -368,17 +557,12 @@ static int ssd1307fb_remove(struct i2c_client *client) | |||
368 | } | 557 | } |
369 | 558 | ||
370 | static const struct i2c_device_id ssd1307fb_i2c_id[] = { | 559 | static const struct i2c_device_id ssd1307fb_i2c_id[] = { |
560 | { "ssd1306fb", 0 }, | ||
371 | { "ssd1307fb", 0 }, | 561 | { "ssd1307fb", 0 }, |
372 | { } | 562 | { } |
373 | }; | 563 | }; |
374 | MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); | 564 | MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); |
375 | 565 | ||
376 | static const struct of_device_id ssd1307fb_of_match[] = { | ||
377 | { .compatible = "solomon,ssd1307fb-i2c" }, | ||
378 | {}, | ||
379 | }; | ||
380 | MODULE_DEVICE_TABLE(of, ssd1307fb_of_match); | ||
381 | |||
382 | static struct i2c_driver ssd1307fb_driver = { | 566 | static struct i2c_driver ssd1307fb_driver = { |
383 | .probe = ssd1307fb_probe, | 567 | .probe = ssd1307fb_probe, |
384 | .remove = ssd1307fb_remove, | 568 | .remove = ssd1307fb_remove, |
diff --git a/include/video/of_display_timing.h b/include/video/of_display_timing.h index 8016eb727cf3..6562ad965889 100644 --- a/include/video/of_display_timing.h +++ b/include/video/of_display_timing.h | |||
@@ -14,6 +14,8 @@ struct display_timings; | |||
14 | 14 | ||
15 | #define OF_USE_NATIVE_MODE -1 | 15 | #define OF_USE_NATIVE_MODE -1 |
16 | 16 | ||
17 | int of_get_display_timing(struct device_node *np, const char *name, | ||
18 | struct display_timing *dt); | ||
17 | struct display_timings *of_get_display_timings(struct device_node *np); | 19 | struct display_timings *of_get_display_timings(struct device_node *np); |
18 | int of_display_timings_exist(struct device_node *np); | 20 | int of_display_timings_exist(struct device_node *np); |
19 | 21 | ||