All of lore.kernel.org
 help / color / mirror / Atom feed
From: Peter Robinson <pbrobinson@gmail.com>
To: Joel Savitz <jsavitz@redhat.com>
Cc: linux-rpi-kernel@lists.infradead.org,
	fedora-rpi@googlegroups.com,  serge@raspberrypi.org,
	bcm-kernel-feedback-list@broadcom.com,
	 Joel Savitz <joelsavitz@gmail.com>,
	linux-arm-kernel@lists.infradead.org
Subject: Re: [RFC PATCH] drivers: Raspbery Pi Sense Hat
Date: Thu, 8 Apr 2021 10:21:21 +0100	[thread overview]
Message-ID: <CALeDE9OP0vCiL7dtVzN9r0tkkBT6zXabZT6guFCYx0yWZ3AWsw@mail.gmail.com> (raw)
In-Reply-To: <20210408000353.3571132-1-jsavitz@redhat.com>

 " Hi Joel,

> This is a forward port of the Rapsberry Pi Sense Hat driver from the
> downstream Raspberry Pi kernel tree.

Please copy/reference the original author of the driver that is being
forward ported if it's not you.

> I have slightly consolidated things and done some quick testing of this
> driver on Fedora (which comes with the device tree blobs), and I want to
> do a bit more testing before submiting the driver for inclusion in
> mainline Linux.
>
> I would like to get some feedback from the community on what may need to
> be improved, fixed, or changed as early as possible, so this is a request
> for comments.

Without reviewing the actual drivers I would suggest:
1) split each driver out into a separate patch with a cover letter for
the series
2) Each of the drivers will also need to have their subsystem mailing
lists copied. Something like "./scripts/get_maintainer.pl -f
drivers/input/Kconfig" should give you this information
3) I suspect each of the drivers will also need a DT bindings added to
Documentation/devicetree/bindings/ as a separate patch

Peter

> Signed-off-by: Joel Savitz <joelsavitz@gmail.com>
> ---
>  drivers/input/joystick/Kconfig       |   8 +
>  drivers/input/joystick/Makefile      |   1 +
>  drivers/input/joystick/rpisense-js.c | 147 ++++++++++++++
>  drivers/mfd/Kconfig                  |   8 +
>  drivers/mfd/Makefile                 |   1 +
>  drivers/mfd/rpisense-core.c          | 162 +++++++++++++++
>  drivers/video/fbdev/Kconfig          |  13 ++
>  drivers/video/fbdev/Makefile         |   1 +
>  drivers/video/fbdev/rpisense-fb.c    | 291 +++++++++++++++++++++++++++
>  include/linux/mfd/rpisense.h         |  66 ++++++
>  10 files changed, 698 insertions(+)
>  create mode 100644 drivers/input/joystick/rpisense-js.c
>  create mode 100644 drivers/mfd/rpisense-core.c
>  create mode 100644 drivers/video/fbdev/rpisense-fb.c
>  create mode 100644 include/linux/mfd/rpisense.h
>
> diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
> index 5e38899058c1..1c0025c22bf4 100644
> --- a/drivers/input/joystick/Kconfig
> +++ b/drivers/input/joystick/Kconfig
> @@ -389,4 +389,12 @@ config JOYSTICK_N64
>           Say Y here if you want enable support for the four
>           built-in controller ports on the Nintendo 64 console.
>
> +config JOYSTICK_RPISENSE
> +       tristate "Raspberry Pi Sense HAT joystick"
> +       depends on GPIOLIB && INPUT
> +       select MFD_RPISENSE_CORE
> +
> +       help
> +         This is the joystick driver for the Raspberry Pi Sense HAT
> +
>  endif
> diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
> index 31d720c9e493..b227e98cd179 100644
> --- a/drivers/input/joystick/Makefile
> +++ b/drivers/input/joystick/Makefile
> @@ -27,6 +27,7 @@ obj-$(CONFIG_JOYSTICK_MAPLE)          += maplecontrol.o
>  obj-$(CONFIG_JOYSTICK_N64)             += n64joy.o
>  obj-$(CONFIG_JOYSTICK_PSXPAD_SPI)      += psxpad-spi.o
>  obj-$(CONFIG_JOYSTICK_PXRC)            += pxrc.o
> +obj-$(CONFIG_JOYSTICK_RPISENSE)         += rpisense-js.o
>  obj-$(CONFIG_JOYSTICK_SIDEWINDER)      += sidewinder.o
>  obj-$(CONFIG_JOYSTICK_SPACEBALL)       += spaceball.o
>  obj-$(CONFIG_JOYSTICK_SPACEORB)                += spaceorb.o
> diff --git a/drivers/input/joystick/rpisense-js.c b/drivers/input/joystick/rpisense-js.c
> new file mode 100644
> index 000000000000..41d4bd1fed46
> --- /dev/null
> +++ b/drivers/input/joystick/rpisense-js.c
> @@ -0,0 +1,147 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Raspberry Pi Sense HAT joystick driver
> + * http://raspberrypi.org
> + *
> + * Copyright (C) 2015 Raspberry Pi
> + *
> + * Author: Serge Schneider
> + * Adapted for mainline Linux by: Joel Savitz
> + */
> +
> +#include <linux/module.h>
> +#include <linux/mfd/rpisense.h>
> +
> +static struct rpisense *rpisense;
> +static unsigned char keymap[5] = {KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT,};
> +
> +static void keys_work_fn(struct work_struct *work)
> +{
> +       int i;
> +       static s32 prev_keys;
> +       struct rpisense_js *rpisense_js = &rpisense->joystick;
> +       s32 keys = rpisense_reg_read(rpisense, RPISENSE_KEYS);
> +       s32 changes = keys ^ prev_keys;
> +
> +       prev_keys = keys;
> +       for (i = 0; i < 5; i++) {
> +               if (changes & 1) {
> +                       input_report_key(rpisense_js->keys_dev,
> +                                        keymap[i], keys & 1);
> +               }
> +               changes >>= 1;
> +               keys >>= 1;
> +       }
> +       input_sync(rpisense_js->keys_dev);
> +}
> +
> +static irqreturn_t keys_irq_handler(int irq, void *pdev)
> +{
> +       struct rpisense_js *rpisense_js = &rpisense->joystick;
> +
> +       schedule_work(&rpisense_js->keys_work_s);
> +       return IRQ_HANDLED;
> +}
> +
> +static int rpisense_js_probe(struct platform_device *pdev)
> +{
> +       int ret;
> +       int i;
> +       struct rpisense_js *rpisense_js;
> +
> +       rpisense = rpisense_get_dev();
> +       rpisense_js = &rpisense->joystick;
> +
> +       INIT_WORK(&rpisense_js->keys_work_s, keys_work_fn);
> +
> +       rpisense_js->keys_dev = input_allocate_device();
> +       if (!rpisense_js->keys_dev) {
> +               dev_err(&pdev->dev, "Could not allocate input device.\n");
> +               return -ENOMEM;
> +       }
> +
> +       rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY);
> +       for (i = 0; i < ARRAY_SIZE(keymap); i++) {
> +               set_bit(keymap[i],
> +                       rpisense_js->keys_dev->keybit);
> +       }
> +
> +       rpisense_js->keys_dev->name = "Raspberry Pi Sense HAT Joystick";
> +       rpisense_js->keys_dev->phys = "rpi-sense-joy/input0";
> +       rpisense_js->keys_dev->id.bustype = BUS_I2C;
> +       rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
> +       rpisense_js->keys_dev->keycode = keymap;
> +       rpisense_js->keys_dev->keycodesize = sizeof(unsigned char);
> +       rpisense_js->keys_dev->keycodemax = ARRAY_SIZE(keymap);
> +
> +       ret = input_register_device(rpisense_js->keys_dev);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Could not register input device.\n");
> +               goto err_keys_alloc;
> +       }
> +
> +       ret = gpiod_direction_input(rpisense_js->keys_desc);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Could not set keys-int direction.\n");
> +               goto err_keys_reg;
> +       }
> +
> +       rpisense_js->keys_irq = gpiod_to_irq(rpisense_js->keys_desc);
> +       if (rpisense_js->keys_irq < 0) {
> +               dev_err(&pdev->dev, "Could not determine keys-int IRQ.\n");
> +               ret = rpisense_js->keys_irq;
> +               goto err_keys_reg;
> +       }
> +
> +       ret = devm_request_irq(&pdev->dev, rpisense_js->keys_irq,
> +                              keys_irq_handler, IRQF_TRIGGER_RISING,
> +                              "keys", &pdev->dev);
> +       if (ret) {
> +               dev_err(&pdev->dev, "IRQ request failed.\n");
> +               goto err_keys_reg;
> +       }
> +       return 0;
> +err_keys_reg:
> +       input_unregister_device(rpisense_js->keys_dev);
> +err_keys_alloc:
> +       input_free_device(rpisense_js->keys_dev);
> +       return ret;
> +}
> +
> +static int rpisense_js_remove(struct platform_device *pdev)
> +{
> +       struct rpisense_js *rpisense_js = &rpisense->joystick;
> +
> +       input_unregister_device(rpisense_js->keys_dev);
> +       input_free_device(rpisense_js->keys_dev);
> +       return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id rpisense_js_id[] = {
> +       { .compatible = "raspberrypi,rpi-sense-js" },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, rpisense_js_id);
> +#endif
> +
> +static struct platform_device_id rpisense_js_device_id[] = {
> +       { .name = "rpi-sense-js" },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(platform, rpisense_js_device_id);
> +
> +static struct platform_driver rpisense_js_driver = {
> +       .probe = rpisense_js_probe,
> +       .remove = rpisense_js_remove,
> +       .driver = {
> +               .name = "rpi-sense-js",
> +               .owner = THIS_MODULE,
> +       },
> +};
> +
> +module_platform_driver(rpisense_js_driver);
> +
> +MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
> +MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index b74efa469e90..8c309e389359 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -11,6 +11,14 @@ config MFD_CORE
>         select IRQ_DOMAIN
>         default n
>
> +config MFD_RPISENSE_CORE
> +       tristate "Raspberry Pi Sense HAT core functions"
> +       depends on I2C
> +       select MFD_CORE
> +       help
> +         This is the core driver for the Raspberry Pi Sense HAT. This provides
> +         the necessary functions to communicate with the hardware.
> +
>  config MFD_CS5535
>         tristate "AMD CS5535 and CS5536 southbridge core functions"
>         select MFD_CORE
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 834f5463af28..7140a8fc36ee 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -263,6 +263,7 @@ obj-$(CONFIG_MFD_ROHM_BD71828)      += rohm-bd71828.o
>  obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
>  obj-$(CONFIG_MFD_STMFX)        += stmfx.o
>  obj-$(CONFIG_MFD_KHADAS_MCU)   += khadas-mcu.o
> +obj-$(CONFIG_MFD_RPISENSE_CORE) += rpisense-core.o
>  obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o
>
>  obj-$(CONFIG_SGI_MFD_IOC3)     += ioc3.o
> diff --git a/drivers/mfd/rpisense-core.c b/drivers/mfd/rpisense-core.c
> new file mode 100644
> index 000000000000..aa2c394c5e67
> --- /dev/null
> +++ b/drivers/mfd/rpisense-core.c
> @@ -0,0 +1,162 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Raspberry Pi Sense HAT core driver
> + * http://raspberrypi.org
> + *
> + * Copyright (C) 2015 Raspberry Pi
> + *
> + * Author: Serge Schneider
> + * Adapted for mainline Linux by: Joel Savitz
> + *
> + *  This driver is based on wm8350 implementation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +
> +#include <linux/mfd/rpisense.h>
> +
> +static struct rpisense *rpisense;
> +
> +static void rpisense_client_dev_register(struct rpisense *rpisense,
> +                                        const char *name,
> +                                        struct platform_device **pdev)
> +{
> +       int ret;
> +
> +       *pdev = platform_device_alloc(name, -1);
> +       if (*pdev == NULL) {
> +               dev_err(rpisense->dev, "Failed to allocate %s\n", name);
> +               return;
> +       }
> +
> +       (*pdev)->dev.parent = rpisense->dev;
> +       platform_set_drvdata(*pdev, rpisense);
> +       ret = platform_device_add(*pdev);
> +       if (ret != 0) {
> +               dev_err(rpisense->dev, "Failed to register %s: %d\n",
> +                       name, ret);
> +               platform_device_put(*pdev);
> +               *pdev = NULL;
> +       }
> +}
> +
> +static int rpisense_probe(struct i2c_client *i2c,
> +                              const struct i2c_device_id *id)
> +{
> +       int ret;
> +       struct rpisense_js *rpisense_js;
> +
> +       rpisense = devm_kzalloc(&i2c->dev, sizeof(struct rpisense), GFP_KERNEL);
> +       if (rpisense == NULL)
> +               return -ENOMEM;
> +
> +       i2c_set_clientdata(i2c, rpisense);
> +       rpisense->dev = &i2c->dev;
> +       rpisense->i2c_client = i2c;
> +
> +       ret = rpisense_reg_read(rpisense, RPISENSE_WAI);
> +       if (ret > 0) {
> +               if (ret != 's')
> +                       return -EINVAL;
> +       } else {
> +               return ret;
> +       }
> +       ret = rpisense_reg_read(rpisense, RPISENSE_VER);
> +       if (ret < 0)
> +               return ret;
> +
> +       dev_info(rpisense->dev,
> +                "Raspberry Pi Sense HAT firmware version %i\n", ret);
> +
> +       rpisense_js = &rpisense->joystick;
> +       rpisense_js->keys_desc = devm_gpiod_get(&i2c->dev,
> +                                               "keys-int", GPIOD_IN);
> +       if (IS_ERR(rpisense_js->keys_desc)) {
> +               dev_warn(&i2c->dev, "Failed to get keys-int descriptor.\n");
> +               rpisense_js->keys_desc = gpio_to_desc(23);
> +               if (rpisense_js->keys_desc == NULL) {
> +                       dev_err(&i2c->dev, "GPIO23 fallback failed.\n");
> +                       return PTR_ERR(rpisense_js->keys_desc);
> +               }
> +       }
> +       rpisense_client_dev_register(rpisense, "rpi-sense-js",
> +                                    &(rpisense->joystick.pdev));
> +       rpisense_client_dev_register(rpisense, "rpi-sense-fb",
> +                                    &(rpisense->framebuffer.pdev));
> +
> +       return 0;
> +}
> +
> +static int rpisense_remove(struct i2c_client *i2c)
> +{
> +       struct rpisense *rpisense = i2c_get_clientdata(i2c);
> +
> +       platform_device_unregister(rpisense->joystick.pdev);
> +       return 0;
> +}
> +
> +struct rpisense *rpisense_get_dev(void)
> +{
> +       return rpisense;
> +}
> +EXPORT_SYMBOL_GPL(rpisense_get_dev);
> +
> +s32 rpisense_reg_read(struct rpisense *rpisense, int reg)
> +{
> +       int ret = i2c_smbus_read_byte_data(rpisense->i2c_client, reg);
> +
> +       if (ret < 0)
> +               dev_err(rpisense->dev, "Read from reg %d failed\n", reg);
> +       /* Due to the BCM270x I2C clock stretching bug, some values
> +        * may have MSB set. Clear it to avoid incorrect values.
> +        */
> +       return ret & 0x7F;
> +}
> +EXPORT_SYMBOL_GPL(rpisense_reg_read);
> +
> +int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count)
> +{
> +       int ret = i2c_master_send(rpisense->i2c_client, buf, count);
> +
> +       if (ret < 0)
> +               dev_err(rpisense->dev, "Block write failed\n");
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(rpisense_block_write);
> +
> +static const struct i2c_device_id rpisense_i2c_id[] = {
> +       { "rpi-sense", 0 },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(i2c, rpisense_i2c_id);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id rpisense_core_id[] = {
> +       { .compatible = "raspberrypi,rpi-sense" },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, rpisense_core_id);
> +#endif
> +
> +
> +static struct i2c_driver rpisense_driver = {
> +       .driver = {
> +                  .name = "rpi-sense",
> +                  .owner = THIS_MODULE,
> +       },
> +       .probe = rpisense_probe,
> +       .remove = rpisense_remove,
> +       .id_table = rpisense_i2c_id,
> +};
> +
> +module_i2c_driver(rpisense_driver);
> +
> +MODULE_DESCRIPTION("Raspberry Pi Sense HAT core driver");
> +MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
> +MODULE_LICENSE("GPL");
> +
> diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
> index 4f02db65dede..2238e36e71e4 100644
> --- a/drivers/video/fbdev/Kconfig
> +++ b/drivers/video/fbdev/Kconfig
> @@ -2236,6 +2236,19 @@ config FB_SM712
>           called sm712fb. If you want to compile it as a module, say M
>           here and read <file:Documentation/kbuild/modules.rst>.
>
> +config FB_RPISENSE
> +        tristate "Raspberry Pi Sense HAT framebuffer"
> +        depends on FB
> +        select MFD_RPISENSE_CORE
> +        select FB_SYS_FOPS
> +        select FB_SYS_FILLRECT
> +        select FB_SYS_COPYAREA
> +        select FB_SYS_IMAGEBLIT
> +        select FB_DEFERRED_IO
> +
> +        help
> +          This is the framebuffer driver for the Raspberry Pi Sense HAT
> +
>  source "drivers/video/fbdev/omap/Kconfig"
>  source "drivers/video/fbdev/omap2/Kconfig"
>  source "drivers/video/fbdev/mmp/Kconfig"
> diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
> index 477b9624b703..5b17203b7e43 100644
> --- a/drivers/video/fbdev/Makefile
> +++ b/drivers/video/fbdev/Makefile
> @@ -129,6 +129,7 @@ obj-$(CONFIG_FB_MX3)                  += mx3fb.o
>  obj-$(CONFIG_FB_DA8XX)           += da8xx-fb.o
>  obj-$(CONFIG_FB_SSD1307)         += ssd1307fb.o
>  obj-$(CONFIG_FB_SIMPLE)           += simplefb.o
> +obj-$(CONFIG_FB_RPISENSE)         += rpisense-fb.o
>
>  # the test framebuffer is last
>  obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
> diff --git a/drivers/video/fbdev/rpisense-fb.c b/drivers/video/fbdev/rpisense-fb.c
> new file mode 100644
> index 000000000000..a90a5ec51fd7
> --- /dev/null
> +++ b/drivers/video/fbdev/rpisense-fb.c
> @@ -0,0 +1,291 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Raspberry Pi Sense HAT framebuffer driver
> + * http://raspberrypi.org
> + *
> + * Copyright (C) 2015 Raspberry Pi
> + *
> + * Author: Serge Schneider
> + * Adapted for mainline Linux by: Joel Savitz
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/delay.h>
> +#include <linux/fb.h>
> +#include <linux/init.h>
> +
> +#include <linux/mfd/rpisense.h>
> +
> +static bool lowlight;
> +module_param(lowlight, bool, 0);
> +MODULE_PARM_DESC(lowlight, "Reduce LED matrix brightness to one third");
> +
> +static struct rpisense *rpisense;
> +
> +struct rpisense_fb_param {
> +       char __iomem *vmem;
> +       u8 *vmem_work;
> +       u32 vmemsize;
> +       u8 *gamma;
> +};
> +
> +static u8 gamma_default[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
> +                              0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07,
> +                              0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x11,
> +                              0x12, 0x14, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,};
> +
> +static u8 gamma_low[32] = {0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
> +                          0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
> +                          0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
> +                          0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x0A, 0x0A,};
> +
> +static u8 gamma_user[32];
> +
> +static u32 pseudo_palette[16];
> +
> +static struct rpisense_fb_param rpisense_fb_param = {
> +       .vmem = NULL,
> +       .vmemsize = 128,
> +       .gamma = gamma_default,
> +};
> +
> +static struct fb_deferred_io rpisense_fb_defio;
> +
> +static struct fb_fix_screeninfo rpisense_fb_fix = {
> +       .id =           "RPi-Sense FB",
> +       .type =         FB_TYPE_PACKED_PIXELS,
> +       .visual =       FB_VISUAL_TRUECOLOR,
> +       .xpanstep =     0,
> +       .ypanstep =     0,
> +       .ywrapstep =    0,
> +       .accel =        FB_ACCEL_NONE,
> +       .line_length =  16,
> +};
> +
> +static struct fb_var_screeninfo rpisense_fb_var = {
> +       .xres           = 8,
> +       .yres           = 8,
> +       .xres_virtual   = 8,
> +       .yres_virtual   = 8,
> +       .bits_per_pixel = 16,
> +       .red            = {11, 5, 0},
> +       .green          = {5, 6, 0},
> +       .blue           = {0, 5, 0},
> +};
> +
> +static ssize_t rpisense_fb_write(struct fb_info *info,
> +                                const char __user *buf, size_t count,
> +                                loff_t *ppos)
> +{
> +       ssize_t res = fb_sys_write(info, buf, count, ppos);
> +
> +       schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
> +       return res;
> +}
> +
> +static void rpisense_fb_fillrect(struct fb_info *info,
> +                                const struct fb_fillrect *rect)
> +{
> +       sys_fillrect(info, rect);
> +       schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
> +}
> +
> +static void rpisense_fb_copyarea(struct fb_info *info,
> +                                const struct fb_copyarea *area)
> +{
> +       sys_copyarea(info, area);
> +       schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
> +}
> +
> +static void rpisense_fb_imageblit(struct fb_info *info,
> +                                 const struct fb_image *image)
> +{
> +       sys_imageblit(info, image);
> +       schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
> +}
> +
> +static void rpisense_fb_deferred_io(struct fb_info *info,
> +                               struct list_head *pagelist)
> +{
> +       int i;
> +       int j;
> +       u8 *vmem_work = rpisense_fb_param.vmem_work;
> +       u16 *mem = (u16 *)rpisense_fb_param.vmem;
> +       u8 *gamma = rpisense_fb_param.gamma;
> +
> +       vmem_work[0] = 0;
> +       for (j = 0; j < 8; j++) {
> +               for (i = 0; i < 8; i++) {
> +                       vmem_work[(j * 24) + i + 1] =
> +                               gamma[(mem[(j * 8) + i] >> 11) & 0x1F];
> +                       vmem_work[(j * 24) + (i + 8) + 1] =
> +                               gamma[(mem[(j * 8) + i] >> 6) & 0x1F];
> +                       vmem_work[(j * 24) + (i + 16) + 1] =
> +                               gamma[(mem[(j * 8) + i]) & 0x1F];
> +               }
> +       }
> +       rpisense_block_write(rpisense, vmem_work, 193);
> +}
> +
> +static struct fb_deferred_io rpisense_fb_defio = {
> +       .delay          = HZ/100,
> +       .deferred_io    = rpisense_fb_deferred_io,
> +};
> +
> +static int rpisense_fb_ioctl(struct fb_info *info, unsigned int cmd,
> +                            unsigned long arg)
> +{
> +       switch (cmd) {
> +       case SENSEFB_FBIOGET_GAMMA:
> +               if (copy_to_user((void __user *) arg, rpisense_fb_param.gamma,
> +                                sizeof(u8[32])))
> +                       return -EFAULT;
> +               return 0;
> +       case SENSEFB_FBIOSET_GAMMA:
> +               if (copy_from_user(gamma_user, (void __user *)arg,
> +                                  sizeof(u8[32])))
> +                       return -EFAULT;
> +               rpisense_fb_param.gamma = gamma_user;
> +               schedule_delayed_work(&info->deferred_work,
> +                                     rpisense_fb_defio.delay);
> +               return 0;
> +       case SENSEFB_FBIORESET_GAMMA:
> +               switch (arg) {
> +               case 0:
> +                       rpisense_fb_param.gamma = gamma_default;
> +                       break;
> +               case 1:
> +                       rpisense_fb_param.gamma = gamma_low;
> +                       break;
> +               case 2:
> +                       rpisense_fb_param.gamma = gamma_user;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +               schedule_delayed_work(&info->deferred_work,
> +                                     rpisense_fb_defio.delay);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +       return 0;
> +}
> +
> +static struct fb_ops rpisense_fb_ops = {
> +       .owner          = THIS_MODULE,
> +       .fb_read        = fb_sys_read,
> +       .fb_write       = rpisense_fb_write,
> +       .fb_fillrect    = rpisense_fb_fillrect,
> +       .fb_copyarea    = rpisense_fb_copyarea,
> +       .fb_imageblit   = rpisense_fb_imageblit,
> +       .fb_ioctl       = rpisense_fb_ioctl,
> +};
> +
> +static int rpisense_fb_probe(struct platform_device *pdev)
> +{
> +       struct fb_info *info;
> +       int ret = -ENOMEM;
> +       struct rpisense_fb *rpisense_fb;
> +
> +       rpisense = rpisense_get_dev();
> +       rpisense_fb = &rpisense->framebuffer;
> +
> +       rpisense_fb_param.vmem = vzalloc(rpisense_fb_param.vmemsize);
> +       if (!rpisense_fb_param.vmem)
> +               return ret;
> +
> +       rpisense_fb_param.vmem_work = devm_kmalloc(&pdev->dev, 193, GFP_KERNEL);
> +       if (!rpisense_fb_param.vmem_work)
> +               goto err_malloc;
> +
> +       info = framebuffer_alloc(0, &pdev->dev);
> +       if (!info) {
> +               dev_err(&pdev->dev, "Could not allocate framebuffer.\n");
> +               goto err_malloc;
> +       }
> +       rpisense_fb->info = info;
> +
> +       rpisense_fb_fix.smem_start = (unsigned long)rpisense_fb_param.vmem;
> +       rpisense_fb_fix.smem_len = rpisense_fb_param.vmemsize;
> +
> +       info->fbops = &rpisense_fb_ops;
> +       info->fix = rpisense_fb_fix;
> +       info->var = rpisense_fb_var;
> +       info->fbdefio = &rpisense_fb_defio;
> +       info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
> +       info->screen_base = rpisense_fb_param.vmem;
> +       info->screen_size = rpisense_fb_param.vmemsize;
> +       info->pseudo_palette = pseudo_palette;
> +
> +       if (lowlight)
> +               rpisense_fb_param.gamma = gamma_low;
> +
> +       fb_deferred_io_init(info);
> +
> +       ret = register_framebuffer(info);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Could not register framebuffer.\n");
> +               goto err_fballoc;
> +       }
> +
> +       fb_info(info, "%s frame buffer device\n", info->fix.id);
> +       schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
> +       return 0;
> +err_fballoc:
> +       framebuffer_release(info);
> +err_malloc:
> +       vfree(rpisense_fb_param.vmem);
> +       return ret;
> +}
> +
> +static int rpisense_fb_remove(struct platform_device *pdev)
> +{
> +       struct rpisense_fb *rpisense_fb = &rpisense->framebuffer;
> +       struct fb_info *info = rpisense_fb->info;
> +
> +       if (info) {
> +               unregister_framebuffer(info);
> +               fb_deferred_io_cleanup(info);
> +               framebuffer_release(info);
> +               vfree(rpisense_fb_param.vmem);
> +       }
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id rpisense_fb_id[] = {
> +       { .compatible = "raspberrypi,rpi-sense-fb" },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, rpisense_fb_id);
> +#endif
> +
> +static struct platform_device_id rpisense_fb_device_id[] = {
> +       { .name = "rpi-sense-fb" },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(platform, rpisense_fb_device_id);
> +
> +static struct platform_driver rpisense_fb_driver = {
> +       .probe = rpisense_fb_probe,
> +       .remove = rpisense_fb_remove,
> +       .driver = {
> +               .name = "rpi-sense-fb",
> +               .owner = THIS_MODULE,
> +       },
> +};
> +
> +module_platform_driver(rpisense_fb_driver);
> +
> +MODULE_DESCRIPTION("Raspberry Pi Sense HAT framebuffer driver");
> +MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
> +MODULE_LICENSE("GPL");
> +
> diff --git a/include/linux/mfd/rpisense.h b/include/linux/mfd/rpisense.h
> new file mode 100644
> index 000000000000..0718b6f0eddb
> --- /dev/null
> +++ b/include/linux/mfd/rpisense.h
> @@ -0,0 +1,66 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Raspberry Pi Sense HAT core driver
> + * http://raspberrypi.org
> + *
> + * Copyright (C) 2015 Raspberry Pi
> + *
> + * Author: Serge Schneider
> + * Adapted for mainline Linux by: Joel Savitz
> + */
> +
> +#ifndef __LINUX_MFD_RPISENSE_CORE_H_
> +#define __LINUX_MFD_RPISENSE_CORE_H_
> +
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/platform_device.h>
> +
> +/*
> + * Register values.
> + */
> +#define RPISENSE_FB                    0x00
> +#define RPISENSE_WAI                   0xF0
> +#define RPISENSE_VER                   0xF1
> +#define RPISENSE_KEYS                  0xF2
> +#define RPISENSE_EE_WP                 0xF3
> +
> +#define RPISENSE_ID                    's'
> +
> +#define SENSEFB_FBIO_IOC_MAGIC 0xF1
> +
> +#define SENSEFB_FBIOGET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 0)
> +#define SENSEFB_FBIOSET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 1)
> +#define SENSEFB_FBIORESET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 2)
> +
> +struct rpisense_fb {
> +       struct platform_device *pdev;
> +       struct fb_info *info;
> +};
> +
> +
> +struct rpisense_js {
> +       struct platform_device *pdev;
> +       struct input_dev *keys_dev;
> +       struct gpio_desc *keys_desc;
> +       struct work_struct keys_work_s;
> +       int keys_irq;
> +};
> +
> +
> +struct rpisense {
> +       struct device *dev;
> +       struct i2c_client *i2c_client;
> +
> +       /* Client devices */
> +       struct rpisense_js joystick;
> +       struct rpisense_fb framebuffer;
> +};
> +
> +struct rpisense *rpisense_get_dev(void);
> +s32 rpisense_reg_read(struct rpisense *rpisense, int reg);
> +int rpisense_reg_write(struct rpisense *rpisense, int reg, u16 val);
> +int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count);
> +
> +#endif
> --
> 2.27.0
>
>
> _______________________________________________
> linux-rpi-kernel mailing list
> linux-rpi-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rpi-kernel

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2021-04-08  9:23 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-08  0:03 [RFC PATCH] drivers: Raspbery Pi Sense Hat Joel Savitz
2021-04-08  9:21 ` Peter Robinson [this message]
2021-04-08 16:16 ` Nicolas Saenz Julienne
2021-04-08 19:29 ` Stefan Wahren

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CALeDE9OP0vCiL7dtVzN9r0tkkBT6zXabZT6guFCYx0yWZ3AWsw@mail.gmail.com \
    --to=pbrobinson@gmail.com \
    --cc=bcm-kernel-feedback-list@broadcom.com \
    --cc=fedora-rpi@googlegroups.com \
    --cc=joelsavitz@gmail.com \
    --cc=jsavitz@redhat.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-rpi-kernel@lists.infradead.org \
    --cc=serge@raspberrypi.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.