brcm2708: update 4.1 patches
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.1 / 0122-Merge-pull-request-1043-from-XECDesign-sense-4.0.patch
1 From 910b89499e2db97931afa64b0db1b7d7640af3db Mon Sep 17 00:00:00 2001
2 From: Phil Elwell <pelwell@users.noreply.github.com>
3 Date: Tue, 14 Jul 2015 14:32:47 +0100
4 Subject: [PATCH 122/148] Merge pull request #1043 from XECDesign/sense-4.0
5
6 mfd: Add Raspberry Pi Sense HAT core driver
7 ---
8 arch/arm/boot/dts/overlays/Makefile | 1 +
9 arch/arm/boot/dts/overlays/README | 6 +
10 arch/arm/boot/dts/overlays/rpi-sense-overlay.dts | 47 +++++
11 arch/arm/configs/bcm2709_defconfig | 2 +
12 arch/arm/configs/bcmrpi_defconfig | 2 +
13 drivers/input/joystick/Kconfig | 8 +
14 drivers/input/joystick/Makefile | 1 +
15 drivers/input/joystick/rpisense-js.c | 153 +++++++++++++++
16 drivers/mfd/Kconfig | 8 +
17 drivers/mfd/Makefile | 2 +
18 drivers/mfd/rpisense-core.c | 157 +++++++++++++++
19 drivers/video/fbdev/Kconfig | 13 ++
20 drivers/video/fbdev/Makefile | 1 +
21 drivers/video/fbdev/rpisense-fb.c | 235 +++++++++++++++++++++++
22 include/linux/mfd/rpisense/core.h | 47 +++++
23 include/linux/mfd/rpisense/framebuffer.h | 28 +++
24 include/linux/mfd/rpisense/joystick.h | 35 ++++
25 17 files changed, 746 insertions(+)
26 create mode 100644 arch/arm/boot/dts/overlays/rpi-sense-overlay.dts
27 create mode 100644 drivers/input/joystick/rpisense-js.c
28 create mode 100644 drivers/mfd/rpisense-core.c
29 create mode 100644 drivers/video/fbdev/rpisense-fb.c
30 create mode 100644 include/linux/mfd/rpisense/core.h
31 create mode 100644 include/linux/mfd/rpisense/framebuffer.h
32 create mode 100644 include/linux/mfd/rpisense/joystick.h
33
34 --- a/arch/arm/boot/dts/overlays/Makefile
35 +++ b/arch/arm/boot/dts/overlays/Makefile
36 @@ -39,6 +39,7 @@ dtb-$(RPI_DT_OVERLAYS) += rpi-dac-overla
37 dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb
38 dtb-$(RPI_DT_OVERLAYS) += rpi-ft5406-overlay.dtb
39 dtb-$(RPI_DT_OVERLAYS) += rpi-proto-overlay.dtb
40 +dtb-$(RPI_DT_OVERLAYS) += rpi-sense-overlay.dtb
41 dtb-$(RPI_DT_OVERLAYS) += sdhost-overlay.dtb
42 dtb-$(RPI_DT_OVERLAYS) += spi-bcm2708-overlay.dtb
43 dtb-$(RPI_DT_OVERLAYS) += spi-bcm2835-overlay.dtb
44 --- a/arch/arm/boot/dts/overlays/README
45 +++ b/arch/arm/boot/dts/overlays/README
46 @@ -442,6 +442,12 @@ Load: dtoverlay=rpi-proto
47 Params: <None>
48
49
50 +Name: rpi-sense
51 +Info: Raspberry Pi Sense HAT
52 +Load: dtoverlay=rpi-sense
53 +Params: <None>
54 +
55 +
56 Name: sdhost
57 Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock
58 Load: dtoverlay=sdhost,<param>=<val>
59 --- /dev/null
60 +++ b/arch/arm/boot/dts/overlays/rpi-sense-overlay.dts
61 @@ -0,0 +1,47 @@
62 +// rpi-sense HAT
63 +/dts-v1/;
64 +/plugin/;
65 +
66 +/ {
67 + compatible = "brcm,bcm2708", "brcm,bcm2709";
68 +
69 + fragment@0 {
70 + target = <&i2c1>;
71 + __overlay__ {
72 + #address-cells = <1>;
73 + #size-cells = <0>;
74 + status = "okay";
75 +
76 + rpi-sense@46 {
77 + compatible = "rpi,rpi-sense";
78 + reg = <0x46>;
79 + keys-int-gpios = <&gpio 23 1>;
80 + status = "okay";
81 + };
82 +
83 + lsm9ds1-magn@1c {
84 + compatible = "st,lsm9ds1-magn";
85 + reg = <0x1c>;
86 + status = "okay";
87 + };
88 +
89 + lsm9ds1-accel6a {
90 + compatible = "st,lsm9ds1-accel";
91 + reg = <0x6a>;
92 + status = "okay";
93 + };
94 +
95 + lps25h-press@5c {
96 + compatible = "st,lps25h-press";
97 + reg = <0x5c>;
98 + status = "okay";
99 + };
100 +
101 + hts221-humid@5f {
102 + compatible = "st,hts221-humid";
103 + reg = <0x5f>;
104 + status = "okay";
105 + };
106 + };
107 + };
108 +};
109 --- a/arch/arm/configs/bcm2709_defconfig
110 +++ b/arch/arm/configs/bcm2709_defconfig
111 @@ -533,6 +533,7 @@ CONFIG_JOYSTICK_IFORCE=m
112 CONFIG_JOYSTICK_IFORCE_USB=y
113 CONFIG_JOYSTICK_XPAD=m
114 CONFIG_JOYSTICK_XPAD_FF=y
115 +CONFIG_JOYSTICK_RPISENSE=m
116 CONFIG_INPUT_TOUCHSCREEN=y
117 CONFIG_TOUCHSCREEN_ADS7846=m
118 CONFIG_TOUCHSCREEN_EGALAX=m
119 @@ -789,6 +790,7 @@ CONFIG_VIDEO_MT9V011=m
120 CONFIG_FB=y
121 CONFIG_FB_BCM2708=y
122 CONFIG_FB_SSD1307=m
123 +CONFIG_FB_RPISENSE=m
124 # CONFIG_BACKLIGHT_GENERIC is not set
125 CONFIG_BACKLIGHT_GPIO=m
126 CONFIG_FRAMEBUFFER_CONSOLE=y
127 --- a/arch/arm/configs/bcmrpi_defconfig
128 +++ b/arch/arm/configs/bcmrpi_defconfig
129 @@ -526,6 +526,7 @@ CONFIG_JOYSTICK_IFORCE=m
130 CONFIG_JOYSTICK_IFORCE_USB=y
131 CONFIG_JOYSTICK_XPAD=m
132 CONFIG_JOYSTICK_XPAD_FF=y
133 +CONFIG_JOYSTICK_RPISENSE=m
134 CONFIG_INPUT_TOUCHSCREEN=y
135 CONFIG_TOUCHSCREEN_ADS7846=m
136 CONFIG_TOUCHSCREEN_EGALAX=m
137 @@ -782,6 +783,7 @@ CONFIG_VIDEO_MT9V011=m
138 CONFIG_FB=y
139 CONFIG_FB_BCM2708=y
140 CONFIG_FB_SSD1307=m
141 +CONFIG_FB_RPISENSE=m
142 # CONFIG_BACKLIGHT_GENERIC is not set
143 CONFIG_BACKLIGHT_GPIO=m
144 CONFIG_FRAMEBUFFER_CONSOLE=y
145 --- a/drivers/input/joystick/Kconfig
146 +++ b/drivers/input/joystick/Kconfig
147 @@ -329,4 +329,12 @@ config JOYSTICK_MAPLE
148 To compile this as a module choose M here: the module will be called
149 maplecontrol.
150
151 +config JOYSTICK_RPISENSE
152 + tristate "Raspberry Pi Sense HAT joystick"
153 + depends on GPIOLIB && INPUT
154 + select MFD_RPISENSE_CORE
155 +
156 + help
157 + This is the joystick driver for the Raspberry Pi Sense HAT
158 +
159 endif
160 --- a/drivers/input/joystick/Makefile
161 +++ b/drivers/input/joystick/Makefile
162 @@ -32,4 +32,5 @@ obj-$(CONFIG_JOYSTICK_WARRIOR) += warri
163 obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o
164 obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
165 obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o
166 +obj-$(CONFIG_JOYSTICK_RPISENSE) += rpisense-js.o
167
168 --- /dev/null
169 +++ b/drivers/input/joystick/rpisense-js.c
170 @@ -0,0 +1,153 @@
171 +/*
172 + * Raspberry Pi Sense HAT joystick driver
173 + * http://raspberrypi.org
174 + *
175 + * Copyright (C) 2015 Raspberry Pi
176 + *
177 + * Author: Serge Schneider
178 + *
179 + * This program is free software; you can redistribute it and/or modify it
180 + * under the terms of the GNU General Public License as published by the
181 + * Free Software Foundation; either version 2 of the License, or (at your
182 + * option) any later version.
183 + *
184 + */
185 +
186 +#include <linux/module.h>
187 +
188 +#include <linux/mfd/rpisense/joystick.h>
189 +#include <linux/mfd/rpisense/core.h>
190 +
191 +struct rpisense *rpisense;
192 +unsigned char keymap[5] = {KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT,};
193 +
194 +static void keys_work_fn(struct work_struct *work)
195 +{
196 + int i;
197 + static s32 prev_keys;
198 + struct rpisense_js *rpisense_js = &rpisense->joystick;
199 + s32 keys = rpisense_reg_read(rpisense, RPISENSE_KEYS);
200 + s32 changes = keys ^ prev_keys;
201 +
202 + prev_keys = keys;
203 + for (i = 0; i < 5; i++) {
204 + if (changes & 1) {
205 + input_report_key(rpisense_js->keys_dev,
206 + keymap[i], keys & 1);
207 + }
208 + changes >>= 1;
209 + keys >>= 1;
210 + }
211 + input_sync(rpisense_js->keys_dev);
212 +}
213 +
214 +static irqreturn_t keys_irq_handler(int irq, void *pdev)
215 +{
216 + struct rpisense_js *rpisense_js = &rpisense->joystick;
217 +
218 + schedule_work(&rpisense_js->keys_work_s);
219 + return IRQ_HANDLED;
220 +}
221 +
222 +static int rpisense_js_probe(struct platform_device *pdev)
223 +{
224 + int ret;
225 + int i;
226 + struct rpisense_js *rpisense_js;
227 +
228 + rpisense = rpisense_get_dev();
229 + rpisense_js = &rpisense->joystick;
230 +
231 + INIT_WORK(&rpisense_js->keys_work_s, keys_work_fn);
232 +
233 + rpisense_js->keys_dev = input_allocate_device();
234 + if (!rpisense_js->keys_dev) {
235 + dev_err(&pdev->dev, "Could not allocate input device.\n");
236 + return -ENOMEM;
237 + }
238 +
239 + rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY);
240 + for (i = 0; i < ARRAY_SIZE(keymap); i++) {
241 + set_bit(keymap[i],
242 + rpisense_js->keys_dev->keybit);
243 + }
244 +
245 + rpisense_js->keys_dev->name = "Raspberry Pi Sense HAT Joystick";
246 + rpisense_js->keys_dev->phys = "rpi-sense-joy/input0";
247 + rpisense_js->keys_dev->id.bustype = BUS_I2C;
248 + rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
249 + rpisense_js->keys_dev->keycode = keymap;
250 + rpisense_js->keys_dev->keycodesize = sizeof(unsigned char);
251 + rpisense_js->keys_dev->keycodemax = ARRAY_SIZE(keymap);
252 +
253 + ret = input_register_device(rpisense_js->keys_dev);
254 + if (ret) {
255 + dev_err(&pdev->dev, "Could not register input device.\n");
256 + goto err_keys_alloc;
257 + }
258 +
259 + ret = gpiod_direction_input(rpisense_js->keys_desc);
260 + if (ret) {
261 + dev_err(&pdev->dev, "Could not set keys-int direction.\n");
262 + goto err_keys_reg;
263 + }
264 +
265 + rpisense_js->keys_irq = gpiod_to_irq(rpisense_js->keys_desc);
266 + if (rpisense_js->keys_irq < 0) {
267 + dev_err(&pdev->dev, "Could not determine keys-int IRQ.\n");
268 + ret = rpisense_js->keys_irq;
269 + goto err_keys_reg;
270 + }
271 +
272 + ret = devm_request_irq(&pdev->dev, rpisense_js->keys_irq,
273 + keys_irq_handler, IRQF_TRIGGER_RISING,
274 + "keys", &pdev->dev);
275 + if (ret) {
276 + dev_err(&pdev->dev, "IRQ request failed.\n");
277 + goto err_keys_reg;
278 + }
279 + return 0;
280 +err_keys_reg:
281 + input_unregister_device(rpisense_js->keys_dev);
282 +err_keys_alloc:
283 + input_free_device(rpisense_js->keys_dev);
284 + return ret;
285 +}
286 +
287 +static int rpisense_js_remove(struct platform_device *pdev)
288 +{
289 + struct rpisense_js *rpisense_js = &rpisense->joystick;
290 +
291 + input_unregister_device(rpisense_js->keys_dev);
292 + input_free_device(rpisense_js->keys_dev);
293 + return 0;
294 +}
295 +
296 +#ifdef CONFIG_OF
297 +static const struct of_device_id rpisense_js_id[] = {
298 + { .compatible = "rpi,rpi-sense-js" },
299 + { },
300 +};
301 +MODULE_DEVICE_TABLE(of, rpisense_js_id);
302 +#endif
303 +
304 +static struct platform_device_id rpisense_js_device_id[] = {
305 + { .name = "rpi-sense-js" },
306 + { },
307 +};
308 +MODULE_DEVICE_TABLE(platform, rpisense_js_device_id);
309 +
310 +static struct platform_driver rpisense_js_driver = {
311 + .probe = rpisense_js_probe,
312 + .remove = rpisense_js_remove,
313 + .driver = {
314 + .name = "rpi-sense-js",
315 + .owner = THIS_MODULE,
316 + },
317 +};
318 +
319 +module_platform_driver(rpisense_js_driver);
320 +
321 +MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
322 +MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
323 +MODULE_LICENSE("GPL");
324 --- a/drivers/mfd/Kconfig
325 +++ b/drivers/mfd/Kconfig
326 @@ -10,6 +10,14 @@ config MFD_CORE
327 select IRQ_DOMAIN
328 default n
329
330 +config MFD_RPISENSE_CORE
331 + tristate "Raspberry Pi Sense HAT core functions"
332 + depends on I2C
333 + select MFD_CORE
334 + help
335 + This is the core driver for the Raspberry Pi Sense HAT. This provides
336 + the necessary functions to communicate with the hardware.
337 +
338 config MFD_CS5535
339 tristate "AMD CS5535 and CS5536 southbridge core functions"
340 select MFD_CORE
341 --- a/drivers/mfd/Makefile
342 +++ b/drivers/mfd/Makefile
343 @@ -185,3 +185,5 @@ obj-$(CONFIG_MFD_SKY81452) += sky81452.o
344 intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
345 obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
346 obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
347 +
348 +obj-$(CONFIG_MFD_RPISENSE_CORE) += rpisense-core.o
349 --- /dev/null
350 +++ b/drivers/mfd/rpisense-core.c
351 @@ -0,0 +1,157 @@
352 +/*
353 + * Raspberry Pi Sense HAT core driver
354 + * http://raspberrypi.org
355 + *
356 + * Copyright (C) 2015 Raspberry Pi
357 + *
358 + * Author: Serge Schneider
359 + *
360 + * This program is free software; you can redistribute it and/or modify it
361 + * under the terms of the GNU General Public License as published by the
362 + * Free Software Foundation; either version 2 of the License, or (at your
363 + * option) any later version.
364 + *
365 + * This driver is based on wm8350 implementation.
366 + */
367 +
368 +#include <linux/module.h>
369 +#include <linux/moduleparam.h>
370 +#include <linux/err.h>
371 +#include <linux/init.h>
372 +#include <linux/i2c.h>
373 +#include <linux/platform_device.h>
374 +#include <linux/mfd/rpisense/core.h>
375 +#include <linux/slab.h>
376 +
377 +struct rpisense *rpisense;
378 +
379 +static void rpisense_client_dev_register(struct rpisense *rpisense,
380 + const char *name,
381 + struct platform_device **pdev)
382 +{
383 + int ret;
384 +
385 + *pdev = platform_device_alloc(name, -1);
386 + if (*pdev == NULL) {
387 + dev_err(rpisense->dev, "Failed to allocate %s\n", name);
388 + return;
389 + }
390 +
391 + (*pdev)->dev.parent = rpisense->dev;
392 + platform_set_drvdata(*pdev, rpisense);
393 + ret = platform_device_add(*pdev);
394 + if (ret != 0) {
395 + dev_err(rpisense->dev, "Failed to register %s: %d\n",
396 + name, ret);
397 + platform_device_put(*pdev);
398 + *pdev = NULL;
399 + }
400 +}
401 +
402 +static int rpisense_probe(struct i2c_client *i2c,
403 + const struct i2c_device_id *id)
404 +{
405 + int ret;
406 + struct rpisense_js *rpisense_js;
407 +
408 + rpisense = devm_kzalloc(&i2c->dev, sizeof(struct rpisense), GFP_KERNEL);
409 + if (rpisense == NULL)
410 + return -ENOMEM;
411 +
412 + i2c_set_clientdata(i2c, rpisense);
413 + rpisense->dev = &i2c->dev;
414 + rpisense->i2c_client = i2c;
415 +
416 + ret = rpisense_reg_read(rpisense, RPISENSE_WAI);
417 + if (ret > 0) {
418 + if (ret != 's')
419 + return -EINVAL;
420 + } else {
421 + return ret;
422 + }
423 + ret = rpisense_reg_read(rpisense, RPISENSE_VER);
424 + if (ret < 0)
425 + return ret;
426 +
427 + dev_info(rpisense->dev,
428 + "Raspberry Pi Sense HAT firmware version %i\n", ret);
429 +
430 + rpisense_js = &rpisense->joystick;
431 + rpisense_js->keys_desc = devm_gpiod_get(&i2c->dev,
432 + "keys-int", GPIOD_IN);
433 + if (IS_ERR(rpisense_js->keys_desc)) {
434 + dev_warn(&i2c->dev, "Failed to get keys-int descriptor.\n");
435 + rpisense_js->keys_desc = gpio_to_desc(23);
436 + if (rpisense_js->keys_desc == NULL) {
437 + dev_err(&i2c->dev, "GPIO23 fallback failed.\n");
438 + return PTR_ERR(rpisense_js->keys_desc);
439 + }
440 + }
441 + rpisense_client_dev_register(rpisense, "rpi-sense-js",
442 + &(rpisense->joystick.pdev));
443 + rpisense_client_dev_register(rpisense, "rpi-sense-fb",
444 + &(rpisense->framebuffer.pdev));
445 +
446 + return 0;
447 +}
448 +
449 +static int rpisense_remove(struct i2c_client *i2c)
450 +{
451 + struct rpisense *rpisense = i2c_get_clientdata(i2c);
452 +
453 + platform_device_unregister(rpisense->joystick.pdev);
454 + return 0;
455 +}
456 +
457 +struct rpisense *rpisense_get_dev(void)
458 +{
459 + return rpisense;
460 +}
461 +EXPORT_SYMBOL_GPL(rpisense_get_dev);
462 +
463 +s32 rpisense_reg_read(struct rpisense *rpisense, int reg)
464 +{
465 + int ret = i2c_smbus_read_byte_data(rpisense->i2c_client, reg);
466 +
467 + if (ret < 0)
468 + dev_err(rpisense->dev, "Read from reg %d failed\n", reg);
469 + /* Due to the BCM270x I2C clock stretching bug, some values
470 + * may have MSB set. Clear it to avoid incorrect values.
471 + * */
472 + return ret & 0x7F;
473 +}
474 +EXPORT_SYMBOL_GPL(rpisense_reg_read);
475 +
476 +int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count)
477 +{
478 + int ret = i2c_master_send(rpisense->i2c_client, buf, count);
479 +
480 + if (ret < 0)
481 + dev_err(rpisense->dev, "Block write failed\n");
482 + return ret;
483 +}
484 +EXPORT_SYMBOL_GPL(rpisense_block_write);
485 +
486 +static const struct i2c_device_id rpisense_i2c_id[] = {
487 + { "rpi-sense", 0 },
488 + { }
489 +};
490 +MODULE_DEVICE_TABLE(i2c, rpisense_i2c_id);
491 +
492 +
493 +static struct i2c_driver rpisense_driver = {
494 + .driver = {
495 + .name = "rpi-sense",
496 + .owner = THIS_MODULE,
497 + },
498 + .probe = rpisense_probe,
499 + .remove = rpisense_remove,
500 + .id_table = rpisense_i2c_id,
501 +};
502 +
503 +module_i2c_driver(rpisense_driver);
504 +
505 +MODULE_DESCRIPTION("Raspberry Pi Sense HAT core driver");
506 +MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
507 +MODULE_LICENSE("GPL");
508 +
509 --- a/drivers/video/fbdev/Kconfig
510 +++ b/drivers/video/fbdev/Kconfig
511 @@ -2495,3 +2495,16 @@ config FB_SSD1307
512 help
513 This driver implements support for the Solomon SSD1307
514 OLED controller over I2C.
515 +
516 +config FB_RPISENSE
517 + tristate "Raspberry Pi Sense HAT framebuffer"
518 + depends on FB
519 + select MFD_RPISENSE_CORE
520 + select FB_SYS_FOPS
521 + select FB_SYS_FILLRECT
522 + select FB_SYS_COPYAREA
523 + select FB_SYS_IMAGEBLIT
524 + select FB_DEFERRED_IO
525 +
526 + help
527 + This is the framebuffer driver for the Raspberry Pi Sense HAT
528 --- a/drivers/video/fbdev/Makefile
529 +++ b/drivers/video/fbdev/Makefile
530 @@ -150,6 +150,7 @@ obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
531 obj-$(CONFIG_FB_MXS) += mxsfb.o
532 obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o
533 obj-$(CONFIG_FB_SIMPLE) += simplefb.o
534 +obj-$(CONFIG_FB_RPISENSE) += rpisense-fb.o
535
536 # the test framebuffer is last
537 obj-$(CONFIG_FB_VIRTUAL) += vfb.o
538 --- /dev/null
539 +++ b/drivers/video/fbdev/rpisense-fb.c
540 @@ -0,0 +1,235 @@
541 +/*
542 + * Raspberry Pi Sense HAT framebuffer driver
543 + * http://raspberrypi.org
544 + *
545 + * Copyright (C) 2015 Raspberry Pi
546 + *
547 + * Author: Serge Schneider
548 + *
549 + * This program is free software; you can redistribute it and/or modify it
550 + * under the terms of the GNU General Public License as published by the
551 + * Free Software Foundation; either version 2 of the License, or (at your
552 + * option) any later version.
553 + *
554 + */
555 +
556 +#include <linux/module.h>
557 +#include <linux/kernel.h>
558 +#include <linux/errno.h>
559 +#include <linux/string.h>
560 +#include <linux/mm.h>
561 +#include <linux/slab.h>
562 +#include <linux/delay.h>
563 +#include <linux/fb.h>
564 +#include <linux/init.h>
565 +
566 +#include <linux/mfd/rpisense/framebuffer.h>
567 +#include <linux/mfd/rpisense/core.h>
568 +
569 +struct rpisense *rpisense;
570 +
571 +struct rpisense_fb_param {
572 + char __iomem *vmem;
573 + u8 *vmem_work;
574 + u32 vmemsize;
575 + u8 gamma[32];
576 +};
577 +
578 +static struct rpisense_fb_param rpisense_fb_param = {
579 + .vmem = NULL,
580 + .vmemsize = 128,
581 + .gamma = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
582 + 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07,
583 + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x11,
584 + 0x12, 0x14, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,},
585 +};
586 +
587 +static struct fb_deferred_io rpisense_fb_defio;
588 +
589 +static struct fb_fix_screeninfo rpisense_fb_fix = {
590 + .id = "RPi-Sense FB",
591 + .type = FB_TYPE_PACKED_PIXELS,
592 + .visual = FB_VISUAL_TRUECOLOR,
593 + .xpanstep = 0,
594 + .ypanstep = 0,
595 + .ywrapstep = 0,
596 + .accel = FB_ACCEL_NONE,
597 + .line_length = 16,
598 +};
599 +
600 +static struct fb_var_screeninfo rpisense_fb_var = {
601 + .xres = 8,
602 + .yres = 8,
603 + .xres_virtual = 8,
604 + .yres_virtual = 8,
605 + .bits_per_pixel = 16,
606 + .red = {11, 5, 0},
607 + .green = {5, 6, 0},
608 + .blue = {0, 5, 0},
609 +};
610 +
611 +static ssize_t rpisense_fb_write(struct fb_info *info,
612 + const char __user *buf, size_t count,
613 + loff_t *ppos)
614 +{
615 + ssize_t res = fb_sys_write(info, buf, count, ppos);
616 +
617 + schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
618 + return res;
619 +}
620 +
621 +static void rpisense_fb_fillrect(struct fb_info *info,
622 + const struct fb_fillrect *rect)
623 +{
624 + sys_fillrect(info, rect);
625 + schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
626 +}
627 +
628 +static void rpisense_fb_copyarea(struct fb_info *info,
629 + const struct fb_copyarea *area)
630 +{
631 + sys_copyarea(info, area);
632 + schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
633 +}
634 +
635 +static void rpisense_fb_imageblit(struct fb_info *info,
636 + const struct fb_image *image)
637 +{
638 + sys_imageblit(info, image);
639 + schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
640 +}
641 +
642 +static void rpisense_fb_deferred_io(struct fb_info *info,
643 + struct list_head *pagelist)
644 +{
645 + int i;
646 + int j;
647 + u8 *vmem_work = rpisense_fb_param.vmem_work;
648 + u16 *mem = (u16 *)rpisense_fb_param.vmem;
649 + u8 *gamma = rpisense_fb_param.gamma;
650 +
651 + vmem_work[0] = 0;
652 + for (j = 0; j < 8; j++) {
653 + for (i = 0; i < 8; i++) {
654 + vmem_work[(j * 24) + i + 1] =
655 + gamma[(mem[(j * 8) + i] >> 11) & 0x1F];
656 + vmem_work[(j * 24) + (i + 8) + 1] =
657 + gamma[(mem[(j * 8) + i] >> 6) & 0x1F];
658 + vmem_work[(j * 24) + (i + 16) + 1] =
659 + gamma[(mem[(j * 8) + i]) & 0x1F];
660 + }
661 + }
662 + rpisense_block_write(rpisense, vmem_work, 193);
663 +}
664 +
665 +static struct fb_deferred_io rpisense_fb_defio = {
666 + .delay = HZ/100,
667 + .deferred_io = rpisense_fb_deferred_io,
668 +};
669 +
670 +static struct fb_ops rpisense_fb_ops = {
671 + .owner = THIS_MODULE,
672 + .fb_read = fb_sys_read,
673 + .fb_write = rpisense_fb_write,
674 + .fb_fillrect = rpisense_fb_fillrect,
675 + .fb_copyarea = rpisense_fb_copyarea,
676 + .fb_imageblit = rpisense_fb_imageblit,
677 +};
678 +
679 +static int rpisense_fb_probe(struct platform_device *pdev)
680 +{
681 + struct fb_info *info;
682 + int ret = -ENOMEM;
683 + struct rpisense_fb *rpisense_fb;
684 +
685 + rpisense = rpisense_get_dev();
686 + rpisense_fb = &rpisense->framebuffer;
687 +
688 + rpisense_fb_param.vmem = vzalloc(rpisense_fb_param.vmemsize);
689 + if (!rpisense_fb_param.vmem)
690 + return ret;
691 +
692 + rpisense_fb_param.vmem_work = devm_kmalloc(&pdev->dev, 193, GFP_KERNEL);
693 + if (!rpisense_fb_param.vmem_work)
694 + goto err_malloc;
695 +
696 + info = framebuffer_alloc(0, &pdev->dev);
697 + if (!info) {
698 + dev_err(&pdev->dev, "Could not allocate framebuffer.\n");
699 + goto err_malloc;
700 + }
701 + rpisense_fb->info = info;
702 +
703 + rpisense_fb_fix.smem_start = (unsigned long)rpisense_fb_param.vmem;
704 + rpisense_fb_fix.smem_len = rpisense_fb_param.vmemsize;
705 +
706 + info->fbops = &rpisense_fb_ops;
707 + info->fix = rpisense_fb_fix;
708 + info->var = rpisense_fb_var;
709 + info->fbdefio = &rpisense_fb_defio;
710 + info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
711 + info->screen_base = rpisense_fb_param.vmem;
712 + info->screen_size = rpisense_fb_param.vmemsize;
713 +
714 + fb_deferred_io_init(info);
715 +
716 + ret = register_framebuffer(info);
717 + if (ret < 0) {
718 + dev_err(&pdev->dev, "Could not register framebuffer.\n");
719 + goto err_fballoc;
720 + }
721 +
722 + fb_info(info, "%s frame buffer device\n", info->fix.id);
723 + schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
724 + return 0;
725 +err_fballoc:
726 + framebuffer_release(info);
727 +err_malloc:
728 + vfree(rpisense_fb_param.vmem);
729 + return ret;
730 +}
731 +
732 +static int rpisense_fb_remove(struct platform_device *pdev)
733 +{
734 + struct rpisense_fb *rpisense_fb = &rpisense->framebuffer;
735 + struct fb_info *info = rpisense_fb->info;
736 +
737 + if (info) {
738 + unregister_framebuffer(info);
739 + fb_deferred_io_cleanup(info);
740 + framebuffer_release(info);
741 + vfree(rpisense_fb_param.vmem);
742 + }
743 +
744 + return 0;
745 +}
746 +
747 +#ifdef CONFIG_OF
748 +static const struct of_device_id rpisense_fb_id[] = {
749 + { .compatible = "rpi,rpi-sense-fb" },
750 + { },
751 +};
752 +MODULE_DEVICE_TABLE(of, rpisense_fb_id);
753 +#endif
754 +
755 +static struct platform_device_id rpisense_fb_device_id[] = {
756 + { .name = "rpi-sense-fb" },
757 + { },
758 +};
759 +MODULE_DEVICE_TABLE(platform, rpisense_fb_device_id);
760 +
761 +static struct platform_driver rpisense_fb_driver = {
762 + .probe = rpisense_fb_probe,
763 + .remove = rpisense_fb_remove,
764 + .driver = {
765 + .name = "rpi-sense-fb",
766 + .owner = THIS_MODULE,
767 + },
768 +};
769 +
770 +module_platform_driver(rpisense_fb_driver);
771 +
772 +MODULE_DESCRIPTION("Raspberry Pi Sense HAT framebuffer driver");
773 +MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
774 +MODULE_LICENSE("GPL");
775 +
776 --- /dev/null
777 +++ b/include/linux/mfd/rpisense/core.h
778 @@ -0,0 +1,47 @@
779 +/*
780 + * Raspberry Pi Sense HAT core driver
781 + * http://raspberrypi.org
782 + *
783 + * Copyright (C) 2015 Raspberry Pi
784 + *
785 + * Author: Serge Schneider
786 + *
787 + * This program is free software; you can redistribute it and/or modify it
788 + * under the terms of the GNU General Public License as published by the
789 + * Free Software Foundation; either version 2 of the License, or (at your
790 + * option) any later version.
791 + *
792 + */
793 +
794 +#ifndef __LINUX_MFD_RPISENSE_CORE_H_
795 +#define __LINUX_MFD_RPISENSE_CORE_H_
796 +
797 +#include <linux/mfd/rpisense/joystick.h>
798 +#include <linux/mfd/rpisense/framebuffer.h>
799 +
800 +/*
801 + * Register values.
802 + */
803 +#define RPISENSE_FB 0x00
804 +#define RPISENSE_WAI 0xF0
805 +#define RPISENSE_VER 0xF1
806 +#define RPISENSE_KEYS 0xF2
807 +#define RPISENSE_EE_WP 0xF3
808 +
809 +#define RPISENSE_ID 's'
810 +
811 +struct rpisense {
812 + struct device *dev;
813 + struct i2c_client *i2c_client;
814 +
815 + /* Client devices */
816 + struct rpisense_js joystick;
817 + struct rpisense_fb framebuffer;
818 +};
819 +
820 +struct rpisense *rpisense_get_dev(void);
821 +s32 rpisense_reg_read(struct rpisense *rpisense, int reg);
822 +int rpisense_reg_write(struct rpisense *rpisense, int reg, u16 val);
823 +int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count);
824 +
825 +#endif
826 --- /dev/null
827 +++ b/include/linux/mfd/rpisense/framebuffer.h
828 @@ -0,0 +1,28 @@
829 +/*
830 + * Raspberry Pi Sense HAT framebuffer driver
831 + * http://raspberrypi.org
832 + *
833 + * Copyright (C) 2015 Raspberry Pi
834 + *
835 + * Author: Serge Schneider
836 + *
837 + * This program is free software; you can redistribute it and/or modify it
838 + * under the terms of the GNU General Public License as published by the
839 + * Free Software Foundation; either version 2 of the License, or (at your
840 + * option) any later version.
841 + *
842 + */
843 +
844 +#ifndef __LINUX_RPISENSE_FB_H_
845 +#define __LINUX_RPISENSE_FB_H_
846 +
847 +#include <linux/platform_device.h>
848 +
849 +struct rpisense;
850 +
851 +struct rpisense_fb {
852 + struct platform_device *pdev;
853 + struct fb_info *info;
854 +};
855 +
856 +#endif
857 --- /dev/null
858 +++ b/include/linux/mfd/rpisense/joystick.h
859 @@ -0,0 +1,35 @@
860 +/*
861 + * Raspberry Pi Sense HAT joystick driver
862 + * http://raspberrypi.org
863 + *
864 + * Copyright (C) 2015 Raspberry Pi
865 + *
866 + * Author: Serge Schneider
867 + *
868 + * This program is free software; you can redistribute it and/or modify it
869 + * under the terms of the GNU General Public License as published by the
870 + * Free Software Foundation; either version 2 of the License, or (at your
871 + * option) any later version.
872 + *
873 + */
874 +
875 +#ifndef __LINUX_RPISENSE_JOYSTICK_H_
876 +#define __LINUX_RPISENSE_JOYSTICK_H_
877 +
878 +#include <linux/input.h>
879 +#include <linux/interrupt.h>
880 +#include <linux/gpio/consumer.h>
881 +#include <linux/platform_device.h>
882 +
883 +struct rpisense;
884 +
885 +struct rpisense_js {
886 + struct platform_device *pdev;
887 + struct input_dev *keys_dev;
888 + struct gpio_desc *keys_desc;
889 + struct work_struct keys_work_s;
890 + int keys_irq;
891 +};
892 +
893 +
894 +#endif