* [PATCH] video: fbdev: added driver for sharp memory lcd displays
@ 2019-12-05 13:58 Rodrigo Rolim Mendes de Alencar
2019-12-08 16:55 ` kbuild test robot
0 siblings, 1 reply; 6+ messages in thread
From: Rodrigo Rolim Mendes de Alencar @ 2019-12-05 13:58 UTC (permalink / raw)
To: linux-fbdev, linux-kernel; +Cc: alencar.fmce, Rodrigo Alencar
The supported displays are ls027b7dh01 (tested), ls044q7dh01,
ls013b7dh05, ls013b7dh03
Signed-off-by: Rodrigo Alencar <455.rodrigo.alencar@gmail.com>
---
.../devicetree/bindings/display/smemlcdfb.txt | 46 ++
drivers/video/fbdev/Kconfig | 14 +
drivers/video/fbdev/Makefile | 1 +
drivers/video/fbdev/smemlcdfb.c | 503 ++++++++++++++++++
4 files changed, 564 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/smemlcdfb.txt
create mode 100644 drivers/video/fbdev/smemlcdfb.c
diff --git a/Documentation/devicetree/bindings/display/smemlcdfb.txt b/Documentation/devicetree/bindings/display/smemlcdfb.txt
new file mode 100644
index 000000000000..e33025dd3374
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/smemlcdfb.txt
@@ -0,0 +1,46 @@
+Sharp Memory LCD Linux Framebuffer Driver
+
+Required properties:
+ - compatible: It should be "sharp,<model-no>". The supported displays are
+ ls027b7dh01, ls044q7dh01, ls013b7dh05, ls013b7dh03. Maybe other
+ monochromatic models can be supported with the current code.
+ - reg: SPI chip select number for the device.
+ - spi-max-frequency: Max SPI frequency to use. One must verify the datasheet.
+ - spi-cs-high: Sharp Memory LCD needs chipselect high.
+
+Optional properties:
+ - sharp,frames-per-sec: It should contain the desired frames per second.
+ It does not represent the actual frame rate. Default: 10
+ - sharp,extmode-high: External COM Input signal is expected in EXTCOMIN port.
+ This is recommended to reduce CPU and SPI Load.
+ - pwm: If property "sharp,extmode-high" is specified, this is recommended.
+ It should contain the pwm to use, according to
+ Documentation/devicetree/bindings/pwm/pwm.txt
+ Verify the display datasheet for the EXTCOMIN signal period
+ - disp-gpios: The GPIO used to enable the display, if available. See
+ Documentation/devicetree/bindings/gpio/gpio.txt for details.
+
+Examples:
+
+ls027b7dh01: smemlcd@0 {
+ compatible = "sharp,ls027b7dh01";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ spi-cs-high;
+ disp-gpios = <&gpio0 7>;
+ disp-active-high;
+ sharp,extmode-high;
+ pwms = <&pwm 0 100000000>;
+};
+
+ls013b7dh05: smemlcd@3 {
+ compatible = "sharp,ls013b7dh05";
+ reg = <3>;
+ spi-max-frequency = <1000000>;
+ spi-cs-high;
+ disp-gpios = <&gpio0 13>;
+ disp-active-high;
+ sharp,extmode-high;
+ pwms = <&pwm 0 50000000>;
+ sharp,frames-per-sec = <20>;
+};
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index aa9541bf964b..626a572edc4e 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2264,6 +2264,20 @@ 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_SMEMLCD
+ tristate "Sharp Memory LCD framebuffer support"
+ depends on FB && SPI
+ depends on OF
+ depends on GPIOLIB || COMPILE_TEST
+ select FB_SYS_FOPS
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_DEFERRED_IO
+ select PWM
+ help
+ This driver implements support for the Sharp Memory LCD
+
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 aa6352798cf4..3c0e060e9069 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_FB_PUV3_UNIGFX) += fb-puv3.o
obj-$(CONFIG_FB_HYPERV) += hyperv_fb.o
obj-$(CONFIG_FB_OPENCORES) += ocfb.o
obj-$(CONFIG_FB_SM712) += sm712fb.o
+obj-$(CONFIG_FB_SMEMLCD) += smemlcdfb.o
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
diff --git a/drivers/video/fbdev/smemlcdfb.c b/drivers/video/fbdev/smemlcdfb.c
new file mode 100644
index 000000000000..635f8e27d632
--- /dev/null
+++ b/drivers/video/fbdev/smemlcdfb.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 IMBEL http://www.imbel.gov.br
+ * Rodrigo Alencar <alencar.fmce@imbel.gov.br>
+ */
+
+#include <linux/fb.h>
+#include <linux/bitrev.h>
+#include <linux/gpio/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pwm.h>
+
+#define SMEMLCD_DATA_UPDATE 0x80
+#define SMEMLCD_FRAME_INVERSION 0x40
+#define SMEMLCD_ALL_CLEAR 0x20
+#define SMEMLCD_DUMMY_DATA 0x00
+
+struct smemlcd_info {
+ u32 width;
+ u32 height;
+};
+
+struct smemlcd_par {
+ struct spi_device *spi;
+ struct fb_info *info;
+ struct pwm_device *extcomin_pwm;
+ struct gpio_desc *disp;
+ struct delayed_work d_work;
+ struct mutex update_lock;
+
+ u8 *spi_buf;
+
+ bool extmode;
+ u32 spi_width;
+ u32 vmem_width;
+
+ u8 vcom;
+ u32 start;
+ u32 height;
+};
+
+static struct fb_fix_screeninfo smemlcd_fix = {
+ .id = "Sharp Memory LCD",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_MONO10,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+ .accel = FB_ACCEL_NONE,
+};
+
+static struct fb_var_screeninfo smemlcd_var = {
+ .bits_per_pixel = 1,
+ .red = {0, 1, 0},
+ .green = {0, 1, 0},
+ .blue = {0, 1, 0},
+ .transp = {0, 0, 0},
+ .left_margin = 0,
+ .right_margin = 0,
+ .upper_margin = 0,
+ .lower_margin = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+};
+
+static void smemlcd_update(struct smemlcd_par *par)
+{
+ struct spi_device *spi = par->spi;
+ u8 *vmem = par->info->screen_base;
+ u8 *buf_ptr = par->spi_buf;
+ int ret;
+ u32 i,j;
+
+ if (par->start + par->height > par->info->var.yres) {
+ par->start = 0;
+ par->height = 0;
+ }
+ /* go to start line */
+ vmem += par->start * par->vmem_width;
+ /* update vcom */
+ par->vcom ^= SMEMLCD_FRAME_INVERSION;
+ /* mode selection */
+ *(buf_ptr++) = (par->height)? (SMEMLCD_DATA_UPDATE | par->vcom) : par->vcom;
+
+ /* not all SPI masters have LSB-first mode, bitrev8 is used */
+ for (i = par->start + 1; i < par->start + par->height + 1; i++) {
+ /* gate line address */
+ *(buf_ptr++) = bitrev8(i);
+ /* data writing */
+ for (j = 0; j < par->spi_width; j++)
+ *(buf_ptr++) = bitrev8(*(vmem++));
+ /* dummy data */
+ *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
+ /* video memory alignment */
+ for (; j < par->vmem_width; j++)
+ vmem++;
+ }
+ /* dummy data */
+ *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
+
+ ret = spi_write(spi, &(par->spi_buf[0]), par->height * (par->spi_width + 2) + 2);
+ if (ret < 0)
+ dev_err(&spi->dev, "Couldn't send SPI command.\n");
+
+ par->start = U32_MAX;
+ par->height = 0;
+}
+
+static void smemlcd_frame(struct smemlcd_par *par, u32 req_start, u32 req_height)
+{
+ u32 end = par->start + par->height;
+ u32 req_end = req_start + req_height;
+ if (req_end > par->info->var.yres)
+ req_end = par->info->var.yres;
+ if (par->start > req_start)
+ par->start = req_start;
+ if (end < req_end || end > par->info->var.yres)
+ end = req_end;
+ par->height = end - par->start;
+}
+
+static void smemlcd_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct smemlcd_par *par = info->par;
+ sys_fillrect(info, rect);
+
+ mutex_lock(&par->update_lock);
+ smemlcd_frame(par, rect->dy, rect->height);
+ if(par->extmode)
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+}
+
+static void smemlcd_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct smemlcd_par *par = info->par;
+ sys_imageblit(info, image);
+
+ mutex_lock(&par->update_lock);
+ smemlcd_frame(par, image->dy, image->height);
+ if(par->extmode)
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+}
+
+static void smemlcd_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct smemlcd_par *par = info->par;
+ sys_copyarea(info, area);
+
+ mutex_lock(&par->update_lock);
+ smemlcd_frame(par, area->dy, area->height);
+ if(par->extmode)
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+}
+
+static ssize_t smemlcd_write(struct fb_info *info, const char __user * buf, size_t count, loff_t * ppos)
+{
+ ssize_t ret;
+ struct smemlcd_par *par = info->par;
+ u32 req_start, req_height;
+ u32 offset = (u32) * ppos;
+
+ ret = fb_sys_write(info, buf, count, ppos);
+ if (ret > 0) {
+ mutex_lock(&par->update_lock);
+ req_start = max((int)(offset / par->vmem_width), 0);
+ req_height = ret / par->vmem_width + 1;
+ smemlcd_frame(par, req_start, req_height);
+ if(par->extmode)
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+ }
+
+ return ret;
+}
+
+static int smemlcd_blank(int blank_mode, struct fb_info *info)
+{
+ struct smemlcd_par *par = info->par;
+
+ if (par->disp) {
+ if (blank_mode != FB_BLANK_UNBLANK)
+ gpiod_set_value_cansleep(par->disp, 0);
+ else
+ gpiod_set_value_cansleep(par->disp, 1);
+ }
+
+ return 0;
+}
+
+static void smemlcd_deferred_io(struct fb_info *info, struct list_head *pagelist)
+{
+ struct smemlcd_par *par = info->par;
+
+ mutex_lock(&par->update_lock);
+
+ if (!list_empty(pagelist)) {
+ struct page *cur;
+ u32 req_start;
+ u32 req_height = (PAGE_SIZE / par->vmem_width) + 1;
+
+ list_for_each_entry(cur, pagelist, lru) {
+ req_start = (cur->index << PAGE_SHIFT) / par->vmem_width;
+ smemlcd_frame(par, req_start, req_height);
+ }
+ }
+
+ if(par->extmode)
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+}
+
+static void smemlcd_update_work(struct work_struct *work)
+{
+ struct smemlcd_par *par = container_of(work, struct smemlcd_par, d_work.work);
+ struct fb_info *info = par->info;
+
+ mutex_lock(&par->update_lock);
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+
+ if (!par->extmode)
+ schedule_delayed_work(&par->d_work, info->fbdefio->delay);
+}
+
+static struct fb_ops smemlcd_ops = {
+ .owner = THIS_MODULE,
+ .fb_read = fb_sys_read,
+ .fb_write = smemlcd_write,
+ .fb_fillrect = smemlcd_fillrect,
+ .fb_copyarea = smemlcd_copyarea,
+ .fb_imageblit = smemlcd_imageblit,
+ .fb_blank = smemlcd_blank,
+};
+
+static struct smemlcd_info ls027b7dh01_info = {
+ .width = 400,
+ .height = 240,
+};
+
+static struct smemlcd_info ls044q7dh01_info = {
+ .width = 320,
+ .height = 240,
+};
+
+static struct smemlcd_info ls013b7dh05_info = {
+ .width = 144,
+ .height = 168,
+};
+
+static struct smemlcd_info ls013b7dh03_info = {
+ .width = 128,
+ .height = 128,
+};
+
+static const struct of_device_id smemlcd_of_match[] = {
+ {
+ .compatible = "sharp,ls027b7dh01",
+ .data = (void *)&ls027b7dh01_info,
+ },
+ {
+ .compatible = "sharp,ls044q7dh01",
+ .data = (void *)&ls044q7dh01_info,
+ },
+ {
+ .compatible = "sharp,ls013b7dh05",
+ .data = (void *)&ls013b7dh05_info,
+ },
+ {
+ .compatible = "sharp,ls013b7dh03",
+ .data = (void *)&ls013b7dh03_info,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, smemlcd_of_match);
+
+static int smemlcd_probe(struct spi_device *spi)
+{
+ struct fb_info *info;
+ struct smemlcd_par *par;
+ const struct smemlcd_info *devinfo;
+ struct fb_deferred_io *smemlcd_defio;
+ struct device_node *node = spi->dev.of_node;
+ struct pwm_state state;
+ u32 vmem_size, fps;
+ u8 *vmem;
+ u8 *buf_ptr;
+ int ret;
+
+ info = framebuffer_alloc(sizeof(struct smemlcd_par), &spi->dev);
+ if (!info) {
+ dev_err(&spi->dev, "Failed to allocate framebuffer.\n");
+ return -ENOMEM;
+ }
+
+ par = info->par;
+ par->info = info;
+ par->spi = spi;
+
+ devinfo = of_device_get_match_data(&spi->dev);
+
+ par->disp = devm_gpiod_get_optional(&spi->dev, "disp", GPIOD_OUT_LOW);
+ if (IS_ERR(par->disp)) {
+ ret = PTR_ERR(par->disp);
+ dev_err(&spi->dev, "Failed to get DISP gpio: %d\n", ret);
+ goto free_fb;
+ }
+
+ mutex_init(&par->update_lock);
+ INIT_DELAYED_WORK(&par->d_work, smemlcd_update_work);
+ par->spi_width = devinfo->width / 8;
+ par->vmem_width = ((devinfo->width + 31) & ~31) >> 3;
+ par->vcom = 0;
+ par->start = 0;
+ par->height = 0;
+
+ par->spi_buf = kzalloc(devinfo->height * (par->spi_width + 2) + 2, GFP_KERNEL);
+ if (!par->spi_buf) {
+ ret = -ENOMEM;
+ dev_err(&spi->dev, "Failed to allocate data for spi transfers.\n");
+ goto free_fb;
+ }
+
+ vmem_size = par->vmem_width * devinfo->height;
+
+ vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, get_order(vmem_size));
+ if (!vmem) {
+ ret = -ENOMEM;
+ dev_err(&spi->dev, "Failed to allocate video memory.\n");
+ goto free_spi_buf;
+ }
+
+ par->extmode = of_property_read_bool(node, "sharp,extmode-high");
+
+ if (of_property_read_u32(node, "sharp,frames-per-sec", &fps))
+ fps = 10;
+
+ smemlcd_defio = devm_kzalloc(&spi->dev, sizeof(struct fb_deferred_io), GFP_KERNEL);
+ if (!smemlcd_defio) {
+ dev_err(&spi->dev, "Couldn't allocate deferred io.\n");
+ ret = -ENOMEM;
+ goto free_vmem;
+ }
+
+ smemlcd_defio->delay = HZ / fps;
+ smemlcd_defio->deferred_io = &smemlcd_deferred_io;
+
+ info->var = smemlcd_var;
+ info->var.xres = devinfo->width;
+ info->var.xres_virtual = devinfo->width;
+ info->var.yres = devinfo->height;
+ info->var.yres_virtual = devinfo->height;
+
+ info->screen_base = (u8 __force __iomem *) vmem;
+ info->screen_size = vmem_size;
+
+ info->fbops = &smemlcd_ops;
+ info->fix = smemlcd_fix;
+ info->fix.line_length = par->vmem_width;
+ info->fix.smem_start = __pa(vmem);
+ info->fix.smem_len = vmem_size;
+
+ info->fbdefio = smemlcd_defio;
+
+ fb_deferred_io_init(info);
+
+ spi_set_drvdata(spi, par);
+
+ if (par->extmode) {
+ par->extcomin_pwm = pwm_get(&spi->dev, NULL);
+ if (IS_ERR(par->extcomin_pwm)) {
+ ret = PTR_ERR(par->extcomin_pwm);
+ dev_warn(&spi->dev, "Failed to get EXTCOMIN pwm: %d\n", ret);
+ par->extcomin_pwm = NULL;
+ } else {
+
+ pwm_init_state(par->extcomin_pwm, &state);
+
+ if (!state.period)
+ state.period = NSEC_PER_SEC/fps;
+
+ /* The duty cycle is not really important */
+ state.enabled = true;
+ state.duty_cycle = state.period/2;
+
+ ret = pwm_apply_state(par->extcomin_pwm, &state);
+ if (ret)
+ dev_warn(&spi->dev, "failed to apply EXTCOMIN pwm state: %d\n", ret);
+ }
+ } else {
+ par->extcomin_pwm = NULL;
+ }
+
+ if (par->disp)
+ gpiod_set_value_cansleep(par->disp, 1);
+
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register framebuffer.\n");
+ goto disable_hw;
+ }
+
+ /* spi test by clearing the display */
+ par->spi_buf[0] = SMEMLCD_ALL_CLEAR;
+ par->spi_buf[1] = SMEMLCD_DUMMY_DATA;
+ ret = spi_write(spi, &(par->spi_buf[0]), 2);
+ if (ret < 0){
+ dev_err(&spi->dev, "Couldn't send SPI command.\n");
+ goto disable_hw;
+ }
+
+ dev_info(&spi->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
+
+ if (!par->extmode)
+ schedule_delayed_work(&par->d_work, smemlcd_defio->delay);
+
+ return 0;
+
+disable_hw:
+ if (par->disp)
+ gpiod_set_value_cansleep(par->disp, 0);
+ if (par->extcomin_pwm) {
+ state.enabled = false;
+ state.duty_cycle = 0;
+ pwm_apply_state(par->extcomin_pwm, &state);
+ pwm_put(par->extcomin_pwm);
+ }
+ kfree(smemlcd_defio);
+free_vmem:
+ kfree((void *)vmem);
+free_spi_buf:
+ kfree(par->spi_buf);
+free_fb:
+ cancel_delayed_work_sync(&par->d_work);
+ mutex_destroy(&par->update_lock);
+ framebuffer_release(info);
+
+ return ret;
+}
+
+static int smemlcd_remove(struct spi_device *spi)
+{
+ struct smemlcd_par *par = dev_get_drvdata(&spi->dev);
+ struct fb_info *info = par->info;
+ struct pwm_state state;
+
+ par->extmode = true;
+ cancel_delayed_work_sync(&par->d_work);
+
+ fb_deferred_io_cleanup(info);
+
+ unregister_framebuffer(info);
+
+ if (par->disp)
+ gpiod_set_value_cansleep(par->disp, 0);
+
+ if (par->extcomin_pwm) {
+ pwm_get_state(par->extcomin_pwm, &state);
+ state.enabled = false;
+ state.duty_cycle = 0;
+ pwm_apply_state(par->extcomin_pwm, &state);
+ pwm_put(par->extcomin_pwm);
+ }
+
+ fb_deferred_io_cleanup(info);
+ __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
+
+ kfree(par->spi_buf);
+
+ mutex_destroy(&par->update_lock);
+
+ framebuffer_release(info);
+
+ return 0;
+}
+
+static const struct spi_device_id smemlcd_spi_id[] = {
+ {"ls027b7dh01", 0},
+ {"ls044q7dh01", 0},
+ {"ls013b7dh05", 0},
+ {"ls013b7dh03", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, smemlcd_spi_id);
+
+static struct spi_driver smemlcd_driver = {
+ .probe = smemlcd_probe,
+ .remove = smemlcd_remove,
+ .id_table = smemlcd_spi_id,
+ .driver = {
+ .name = "smemlcdfb",
+ .of_match_table = smemlcd_of_match,
+ },
+};
+module_spi_driver(smemlcd_driver);
+
+MODULE_AUTHOR("Rodrigo Alencar <455.rodrigo.alencar@gmail.com>");
+MODULE_DESCRIPTION("Sharp Memory LCD Linux Framebuffer Driver");
+MODULE_LICENSE("GPL");
--
2.23.0.rc1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] video: fbdev: added driver for sharp memory lcd displays
2019-12-05 13:58 [PATCH] video: fbdev: added driver for sharp memory lcd displays Rodrigo Rolim Mendes de Alencar
@ 2019-12-08 16:55 ` kbuild test robot
2019-12-08 20:25 ` Rodrigo Alencar
0 siblings, 1 reply; 6+ messages in thread
From: kbuild test robot @ 2019-12-08 16:55 UTC (permalink / raw)
To: Rodrigo Rolim Mendes de Alencar
Cc: kbuild-all, linux-fbdev, linux-kernel, alencar.fmce, Rodrigo Alencar
Hi Rodrigo,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linux/master]
[also build test WARNING on robh/for-next linus/master v5.4 next-20191208]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Rodrigo-Rolim-Mendes-de-Alencar/video-fbdev-added-driver-for-sharp-memory-lcd-displays/20191207-112607
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 26bc672134241a080a83b2ab9aa8abede8d30e1c
reproduce:
# apt-get install sparse
# sparse version: v0.6.1-91-g817270f-dirty
make ARCH=x86_64 allmodconfig
make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__'
If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>
sparse warnings: (new ones prefixed by >>)
>> drivers/video/fbdev/smemlcdfb.c:71:29: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned char [usertype] *vmem @@ got signed char [usertype] *vmem @@
>> drivers/video/fbdev/smemlcdfb.c:71:29: sparse: expected unsigned char [usertype] *vmem
>> drivers/video/fbdev/smemlcdfb.c:71:29: sparse: got char [noderef] <asn:2> *screen_base
vim +71 drivers/video/fbdev/smemlcdfb.c
67
68 static void smemlcd_update(struct smemlcd_par *par)
69 {
70 struct spi_device *spi = par->spi;
> 71 u8 *vmem = par->info->screen_base;
72 u8 *buf_ptr = par->spi_buf;
73 int ret;
74 u32 i,j;
75
76 if (par->start + par->height > par->info->var.yres) {
77 par->start = 0;
78 par->height = 0;
79 }
80 /* go to start line */
81 vmem += par->start * par->vmem_width;
82 /* update vcom */
83 par->vcom ^= SMEMLCD_FRAME_INVERSION;
84 /* mode selection */
85 *(buf_ptr++) = (par->height)? (SMEMLCD_DATA_UPDATE | par->vcom) : par->vcom;
86
87 /* not all SPI masters have LSB-first mode, bitrev8 is used */
88 for (i = par->start + 1; i < par->start + par->height + 1; i++) {
89 /* gate line address */
90 *(buf_ptr++) = bitrev8(i);
91 /* data writing */
92 for (j = 0; j < par->spi_width; j++)
93 *(buf_ptr++) = bitrev8(*(vmem++));
94 /* dummy data */
95 *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
96 /* video memory alignment */
97 for (; j < par->vmem_width; j++)
98 vmem++;
99 }
100 /* dummy data */
101 *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
102
103 ret = spi_write(spi, &(par->spi_buf[0]), par->height * (par->spi_width + 2) + 2);
104 if (ret < 0)
105 dev_err(&spi->dev, "Couldn't send SPI command.\n");
106
107 par->start = U32_MAX;
108 par->height = 0;
109 }
110
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org Intel Corporation
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] video: fbdev: added driver for sharp memory lcd displays
2019-12-08 16:55 ` kbuild test robot
@ 2019-12-08 20:25 ` Rodrigo Alencar
0 siblings, 0 replies; 6+ messages in thread
From: Rodrigo Alencar @ 2019-12-08 20:25 UTC (permalink / raw)
To: linux-fbdev, linux-kernel; +Cc: Rodrigo Rolim Mendes de
Hello,
Em dom., 8 de dez. de 2019 às 13:56, kbuild test robot <lkp@intel.com> escreveu:
>
> Hi Rodrigo,
>
> Thank you for the patch! Perhaps something to improve:
>
> [auto build test WARNING on linux/master]
> [also build test WARNING on robh/for-next linus/master v5.4 next-20191208]
> [if your patch is applied to the wrong git tree, please drop us a note to help
> improve the system. BTW, we also suggest to use '--base' option to specify the
> base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
>
> url: https://github.com/0day-ci/linux/commits/Rodrigo-Rolim-Mendes-de-Alencar/video-fbdev-added-driver-for-sharp-memory-lcd-displays/20191207-112607
> base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 26bc672134241a080a83b2ab9aa8abede8d30e1c
> reproduce:
> # apt-get install sparse
> # sparse version: v0.6.1-91-g817270f-dirty
> make ARCH=x86_64 allmodconfig
> make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__'
>
> If you fix the issue, kindly add following tag
> Reported-by: kbuild test robot <lkp@intel.com>
>
>
> sparse warnings: (new ones prefixed by >>)
>
> >> drivers/video/fbdev/smemlcdfb.c:71:29: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned char [usertype] *vmem @@ got signed char [usertype] *vmem @@
> >> drivers/video/fbdev/smemlcdfb.c:71:29: sparse: expected unsigned char [usertype] *vmem
> >> drivers/video/fbdev/smemlcdfb.c:71:29: sparse: got char [noderef] <asn:2> *screen_base
>
> vim +71 drivers/video/fbdev/smemlcdfb.c
>
> 67
> 68 static void smemlcd_update(struct smemlcd_par *par)
> 69 {
> 70 struct spi_device *spi = par->spi;
> > 71 u8 *vmem = par->info->screen_base;
A cast is really necessary here?
> 72 u8 *buf_ptr = par->spi_buf;
> 73 int ret;
> 74 u32 i,j;
> 75
> 76 if (par->start + par->height > par->info->var.yres) {
> 77 par->start = 0;
> 78 par->height = 0;
> 79 }
> 80 /* go to start line */
> 81 vmem += par->start * par->vmem_width;
> 82 /* update vcom */
> 83 par->vcom ^= SMEMLCD_FRAME_INVERSION;
> 84 /* mode selection */
> 85 *(buf_ptr++) = (par->height)? (SMEMLCD_DATA_UPDATE | par->vcom) : par->vcom;
> 86
> 87 /* not all SPI masters have LSB-first mode, bitrev8 is used */
> 88 for (i = par->start + 1; i < par->start + par->height + 1; i++) {
> 89 /* gate line address */
> 90 *(buf_ptr++) = bitrev8(i);
> 91 /* data writing */
> 92 for (j = 0; j < par->spi_width; j++)
> 93 *(buf_ptr++) = bitrev8(*(vmem++));
> 94 /* dummy data */
> 95 *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
> 96 /* video memory alignment */
> 97 for (; j < par->vmem_width; j++)
> 98 vmem++;
> 99 }
> 100 /* dummy data */
> 101 *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
> 102
> 103 ret = spi_write(spi, &(par->spi_buf[0]), par->height * (par->spi_width + 2) + 2);
> 104 if (ret < 0)
> 105 dev_err(&spi->dev, "Couldn't send SPI command.\n");
> 106
> 107 par->start = U32_MAX;
> 108 par->height = 0;
> 109 }
> 110
>
> ---
> 0-DAY kernel test infrastructure Open Source Technology Center
> https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org Intel Corporation
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] video: fbdev: added driver for sharp memory lcd displays
2020-07-26 17:22 ` Sam Ravnborg
@ 2020-07-27 9:22 ` Daniel Vetter
0 siblings, 0 replies; 6+ messages in thread
From: Daniel Vetter @ 2020-07-27 9:22 UTC (permalink / raw)
To: Sam Ravnborg
Cc: Rodrigo Alencar, devicetree, Linux Fbdev development list,
Linux Kernel Mailing List, dri-devel
On Sun, Jul 26, 2020 at 7:22 PM Sam Ravnborg <sam@ravnborg.org> wrote:
>
> Hi Rodrigo.
>
> On Fri, Jul 24, 2020 at 05:34:04PM -0300, Rodrigo Alencar wrote:
> > The supported displays are ls027b7dh01 (tested), ls044q7dh01,
> > ls013b7dh05, ls013b7dh03
> >
> > Signed-off-by: Rodrigo Alencar <455.rodrigo.alencar@gmail.com>
>
> Thanks for submitting this driver.
> Unfortunately I have some bad news for you...
>
> The binding needs to be submitted as a seperate patch, and all new
> bindings must use the DT schema format (.yaml files).
> See Documentation/devicetree/bindings/*.rst for more info.
>
> And on top of this fbdev/ is in maintenance mode so no new drivers.
> New display drivers and panel drivers must go to gpu/drm/
> and use the frameworks available there.
>
> It looks like what we have here is a panel driver.
> Look for other similar sharp panels in drm/panel/ to get an idea how to
> add support for these new panels.
Since it's directly used, probably drm/tiny since we haven't yet
unified panel (used by other drm_device drivers) and tiny (stand-alone
drivers for simple panels).
-Daniel
>
> Soory, but looks forward for an updated patch.
>
> Sam
>
>
> > ---
> > .../devicetree/bindings/display/smemlcdfb.txt | 46 ++
> > drivers/video/fbdev/Kconfig | 14 +
> > drivers/video/fbdev/Makefile | 1 +
> > drivers/video/fbdev/smemlcdfb.c | 485 ++++++++++++++++++
> > 4 files changed, 546 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/display/smemlcdfb.txt
> > create mode 100644 drivers/video/fbdev/smemlcdfb.c
> >
> > diff --git a/Documentation/devicetree/bindings/display/smemlcdfb.txt b/Documentation/devicetree/bindings/display/smemlcdfb.txt
> > new file mode 100644
> > index 000000000000..e33025dd3374
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/display/smemlcdfb.txt
> > @@ -0,0 +1,46 @@
> > +Sharp Memory LCD Linux Framebuffer Driver
> > +
> > +Required properties:
> > + - compatible: It should be "sharp,<model-no>". The supported displays are
> > + ls027b7dh01, ls044q7dh01, ls013b7dh05, ls013b7dh03. Maybe other
> > + monochromatic models can be supported with the current code.
> > + - reg: SPI chip select number for the device.
> > + - spi-max-frequency: Max SPI frequency to use. One must verify the datasheet.
> > + - spi-cs-high: Sharp Memory LCD needs chipselect high.
> > +
> > +Optional properties:
> > + - sharp,frames-per-sec: It should contain the desired frames per second.
> > + It does not represent the actual frame rate. Default: 10
> > + - sharp,extmode-high: External COM Input signal is expected in EXTCOMIN port.
> > + This is recommended to reduce CPU and SPI Load.
> > + - pwm: If property "sharp,extmode-high" is specified, this is recommended.
> > + It should contain the pwm to use, according to
> > + Documentation/devicetree/bindings/pwm/pwm.txt
> > + Verify the display datasheet for the EXTCOMIN signal period
> > + - disp-gpios: The GPIO used to enable the display, if available. See
> > + Documentation/devicetree/bindings/gpio/gpio.txt for details.
> > +
> > +Examples:
> > +
> > +ls027b7dh01: smemlcd@0 {
> > + compatible = "sharp,ls027b7dh01";
> > + reg = <0>;
> > + spi-max-frequency = <1000000>;
> > + spi-cs-high;
> > + disp-gpios = <&gpio0 7>;
> > + disp-active-high;
> > + sharp,extmode-high;
> > + pwms = <&pwm 0 100000000>;
> > +};
> > +
> > +ls013b7dh05: smemlcd@3 {
> > + compatible = "sharp,ls013b7dh05";
> > + reg = <3>;
> > + spi-max-frequency = <1000000>;
> > + spi-cs-high;
> > + disp-gpios = <&gpio0 13>;
> > + disp-active-high;
> > + sharp,extmode-high;
> > + pwms = <&pwm 0 50000000>;
> > + sharp,frames-per-sec = <20>;
> > +};
> > diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
> > index b2c9dd4f0cb5..0fff47a59d8b 100644
> > --- a/drivers/video/fbdev/Kconfig
> > +++ b/drivers/video/fbdev/Kconfig
> > @@ -2221,6 +2221,20 @@ config FB_SSD1307
> > This driver implements support for the Solomon SSD1307
> > OLED controller over I2C.
> >
> > +config FB_SMEMLCD
> > + tristate "Sharp Memory LCD framebuffer support"
> > + depends on FB && SPI
> > + depends on OF
> > + depends on GPIOLIB || COMPILE_TEST
> > + select FB_SYS_FOPS
> > + select FB_SYS_FILLRECT
> > + select FB_SYS_COPYAREA
> > + select FB_SYS_IMAGEBLIT
> > + select FB_DEFERRED_IO
> > + select PWM
> > + help
> > + This driver implements support for the Sharp Memory LCD
> > +
> > config FB_SM712
> > tristate "Silicon Motion SM712 framebuffer support"
> > depends on FB && PCI
> > diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
> > index cad4fb64442a..5c58dfd8ac08 100644
> > --- a/drivers/video/fbdev/Makefile
> > +++ b/drivers/video/fbdev/Makefile
> > @@ -128,6 +128,7 @@ obj-$(CONFIG_FB_OF) += offb.o
> > obj-$(CONFIG_FB_MX3) += mx3fb.o
> > obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
> > obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o
> > +obj-$(CONFIG_FB_SMEMLCD) += smemlcdfb.o
> > obj-$(CONFIG_FB_SIMPLE) += simplefb.o
> >
> > # the test framebuffer is last
> > diff --git a/drivers/video/fbdev/smemlcdfb.c b/drivers/video/fbdev/smemlcdfb.c
> > new file mode 100644
> > index 000000000000..54a0c57b6713
> > --- /dev/null
> > +++ b/drivers/video/fbdev/smemlcdfb.c
> > @@ -0,0 +1,485 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Copyright (C) 2019 IMBEL http://www.imbel.gov.br
> > + * Rodrigo Alencar <alencar.fmce@imbel.gov.br>
> > + */
> > +
> > +#include <linux/fb.h>
> > +#include <linux/bitrev.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/spi/spi.h>
> > +#include <linux/module.h>
> > +#include <linux/property.h>
> > +#include <linux/pwm.h>
> > +
> > +#define SMEMLCD_DATA_UPDATE 0x80
> > +#define SMEMLCD_FRAME_INVERSION 0x40
> > +#define SMEMLCD_ALL_CLEAR 0x20
> > +#define SMEMLCD_DUMMY_DATA 0x00
> > +
> > +struct smemlcd_info {
> > + u32 width;
> > + u32 height;
> > +};
> > +
> > +struct smemlcd_par {
> > + struct spi_device *spi;
> > + struct fb_info *info;
> > + struct pwm_device *extcomin_pwm;
> > + struct gpio_desc *disp;
> > + struct delayed_work d_work;
> > + struct mutex update_lock;
> > +
> > + u8 *spi_buf;
> > +
> > + bool extmode;
> > + u32 spi_width;
> > + u32 vmem_width;
> > +
> > + u8 vcom;
> > + u32 start;
> > + u32 height;
> > +};
> > +
> > +static const struct fb_fix_screeninfo smemlcd_fix = {
> > + .id = "Sharp Memory LCD",
> > + .type = FB_TYPE_PACKED_PIXELS,
> > + .visual = FB_VISUAL_MONO10,
> > + .xpanstep = 0,
> > + .ypanstep = 0,
> > + .ywrapstep = 0,
> > + .accel = FB_ACCEL_NONE,
> > +};
> > +
> > +static const struct fb_var_screeninfo smemlcd_var = {
> > + .bits_per_pixel = 1,
> > + .red = { .length = 1 },
> > + .green = { .length = 1 },
> > + .blue = { .length = 1 },
> > +};
> > +
> > +static void smemlcd_update(struct smemlcd_par *par)
> > +{
> > + struct spi_device *spi = par->spi;
> > + u8 *vmem = par->info->screen_buffer;
> > + u8 *buf_ptr = par->spi_buf;
> > + int ret;
> > + u32 i,j;
> > +
> > + if (par->start + par->height > par->info->var.yres) {
> > + par->start = 0;
> > + par->height = 0;
> > + }
> > + /* go to start line */
> > + vmem += par->start * par->vmem_width;
> > + /* update vcom */
> > + par->vcom ^= SMEMLCD_FRAME_INVERSION;
> > + /* mode selection */
> > + *(buf_ptr++) = (par->height)? (SMEMLCD_DATA_UPDATE | par->vcom) : par->vcom;
> > +
> > + /* not all SPI masters have LSB-first mode, bitrev8 is used */
> > + for (i = par->start + 1; i < par->start + par->height + 1; i++) {
> > + /* gate line address */
> > + *(buf_ptr++) = bitrev8(i);
> > + /* data writing */
> > + for (j = 0; j < par->spi_width; j++)
> > + *(buf_ptr++) = bitrev8(*(vmem++));
> > + /* dummy data */
> > + *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
> > + /* video memory alignment */
> > + for (; j < par->vmem_width; j++)
> > + vmem++;
> > + }
> > + /* dummy data */
> > + *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
> > +
> > + ret = spi_write(spi, &(par->spi_buf[0]), par->height * (par->spi_width + 2) + 2);
> > + if (ret < 0)
> > + dev_err(&spi->dev, "Couldn't send SPI command.\n");
> > +
> > + par->start = U32_MAX;
> > + par->height = 0;
> > +}
> > +
> > +static void smemlcd_frame(struct smemlcd_par *par, u32 req_start, u32 req_height)
> > +{
> > + u32 end = par->start + par->height;
> > + u32 req_end = req_start + req_height;
> > + if (req_end > par->info->var.yres)
> > + req_end = par->info->var.yres;
> > + if (par->start > req_start)
> > + par->start = req_start;
> > + if (end < req_end || end > par->info->var.yres)
> > + end = req_end;
> > + par->height = end - par->start;
> > +}
> > +
> > +static void smemlcd_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
> > +{
> > + struct smemlcd_par *par = info->par;
> > + sys_fillrect(info, rect);
> > +
> > + mutex_lock(&par->update_lock);
> > + smemlcd_frame(par, rect->dy, rect->height);
> > + if(par->extmode)
> > + smemlcd_update(par);
> > + mutex_unlock(&par->update_lock);
> > +}
> > +
> > +static void smemlcd_imageblit(struct fb_info *info, const struct fb_image *image)
> > +{
> > + struct smemlcd_par *par = info->par;
> > + sys_imageblit(info, image);
> > +
> > + mutex_lock(&par->update_lock);
> > + smemlcd_frame(par, image->dy, image->height);
> > + if(par->extmode)
> > + smemlcd_update(par);
> > + mutex_unlock(&par->update_lock);
> > +}
> > +
> > +static void smemlcd_copyarea(struct fb_info *info, const struct fb_copyarea *area)
> > +{
> > + struct smemlcd_par *par = info->par;
> > + sys_copyarea(info, area);
> > +
> > + mutex_lock(&par->update_lock);
> > + smemlcd_frame(par, area->dy, area->height);
> > + if(par->extmode)
> > + smemlcd_update(par);
> > + mutex_unlock(&par->update_lock);
> > +}
> > +
> > +static ssize_t smemlcd_write(struct fb_info *info, const char __user * buf, size_t count, loff_t * ppos)
> > +{
> > + ssize_t ret;
> > + struct smemlcd_par *par = info->par;
> > + u32 req_start, req_height;
> > + u32 offset = (u32) * ppos;
> > +
> > + ret = fb_sys_write(info, buf, count, ppos);
> > + if (ret > 0) {
> > + mutex_lock(&par->update_lock);
> > + req_start = max((int)(offset / par->vmem_width), 0);
> > + req_height = ret / par->vmem_width + 1;
> > + smemlcd_frame(par, req_start, req_height);
> > + if(par->extmode)
> > + smemlcd_update(par);
> > + mutex_unlock(&par->update_lock);
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int smemlcd_blank(int blank_mode, struct fb_info *info)
> > +{
> > + struct smemlcd_par *par = info->par;
> > +
> > + if (par->disp) {
> > + if (blank_mode != FB_BLANK_UNBLANK)
> > + gpiod_set_value_cansleep(par->disp, 0);
> > + else
> > + gpiod_set_value_cansleep(par->disp, 1);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void smemlcd_deferred_io(struct fb_info *info, struct list_head *pagelist)
> > +{
> > + struct smemlcd_par *par = info->par;
> > +
> > + mutex_lock(&par->update_lock);
> > +
> > + if (!list_empty(pagelist)) {
> > + struct page *cur;
> > + u32 req_start;
> > + u32 req_height = (PAGE_SIZE / par->vmem_width) + 1;
> > +
> > + list_for_each_entry(cur, pagelist, lru) {
> > + req_start = (cur->index << PAGE_SHIFT) / par->vmem_width;
> > + smemlcd_frame(par, req_start, req_height);
> > + }
> > + }
> > +
> > + if(par->extmode)
> > + smemlcd_update(par);
> > + mutex_unlock(&par->update_lock);
> > +}
> > +
> > +static void smemlcd_update_work(struct work_struct *work)
> > +{
> > + struct smemlcd_par *par = container_of(work, struct smemlcd_par, d_work.work);
> > + struct fb_info *info = par->info;
> > +
> > + mutex_lock(&par->update_lock);
> > + smemlcd_update(par);
> > + mutex_unlock(&par->update_lock);
> > +
> > + if (!par->extmode)
> > + schedule_delayed_work(&par->d_work, info->fbdefio->delay);
> > +}
> > +
> > +static const struct fb_ops smemlcd_ops = {
> > + .owner = THIS_MODULE,
> > + .fb_read = fb_sys_read,
> > + .fb_write = smemlcd_write,
> > + .fb_fillrect = smemlcd_fillrect,
> > + .fb_copyarea = smemlcd_copyarea,
> > + .fb_imageblit = smemlcd_imageblit,
> > + .fb_blank = smemlcd_blank,
> > +};
> > +
> > +static struct smemlcd_info ls027b7dh01_info = {
> > + .width = 400,
> > + .height = 240,
> > +};
> > +
> > +static struct smemlcd_info ls044q7dh01_info = {
> > + .width = 320,
> > + .height = 240,
> > +};
> > +
> > +static struct smemlcd_info ls013b7dh05_info = {
> > + .width = 144,
> > + .height = 168,
> > +};
> > +
> > +static struct smemlcd_info ls013b7dh03_info = {
> > + .width = 128,
> > + .height = 128,
> > +};
> > +
> > +static const struct of_device_id smemlcd_of_match[] = {
> > + {
> > + .compatible = "sharp,ls027b7dh01",
> > + .data = (void *)&ls027b7dh01_info,
> > + },
> > + {
> > + .compatible = "sharp,ls044q7dh01",
> > + .data = (void *)&ls044q7dh01_info,
> > + },
> > + {
> > + .compatible = "sharp,ls013b7dh05",
> > + .data = (void *)&ls013b7dh05_info,
> > + },
> > + {
> > + .compatible = "sharp,ls013b7dh03",
> > + .data = (void *)&ls013b7dh03_info,
> > + },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, smemlcd_of_match);
> > +
> > +static int smemlcd_probe(struct spi_device *spi)
> > +{
> > + struct device *dev = &spi->dev;
> > + struct fb_info *info;
> > + struct smemlcd_par *par;
> > + const struct smemlcd_info *devinfo;
> > + struct fb_deferred_io *smemlcd_defio;
> > + struct pwm_state state;
> > + u32 vmem_size, fps;
> > + void *vmem;
> > + int ret;
> > +
> > + info = framebuffer_alloc(sizeof(struct smemlcd_par), dev);
> > + if (!info)
> > + return -ENOMEM;
> > +
> > + par = info->par;
> > + par->info = info;
> > + par->spi = spi;
> > +
> > + devinfo = device_get_match_data(dev);
> > +
> > + mutex_init(&par->update_lock);
> > + INIT_DELAYED_WORK(&par->d_work, smemlcd_update_work);
> > + par->spi_width = devinfo->width / 8;
> > + par->vmem_width = ((devinfo->width + 31) & ~31) >> 3;
> > + par->vcom = 0;
> > + par->start = 0;
> > + par->height = 0;
> > +
> > + par->disp = devm_gpiod_get_optional(dev, "disp", GPIOD_OUT_LOW);
> > + if (IS_ERR(par->disp)) {
> > + ret = PTR_ERR(par->disp);
> > + dev_err(dev, "Failed to get DISP gpio: %d\n", ret);
> > + goto free_fb;
> > + }
> > +
> > + par->spi_buf = kzalloc(devinfo->height * (par->spi_width + 2) + 2, GFP_KERNEL);
> > + if (!par->spi_buf) {
> > + ret = -ENOMEM;
> > + dev_err(dev, "Failed to allocate data for spi transfers.\n");
> > + goto free_fb;
> > + }
> > +
> > + vmem_size = par->vmem_width * devinfo->height;
> > +
> > + vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, get_order(vmem_size));
> > + if (!vmem) {
> > + ret = -ENOMEM;
> > + dev_err(dev, "Failed to allocate video memory.\n");
> > + goto free_spi_buf;
> > + }
> > +
> > + par->extmode = device_property_read_bool(dev, "sharp,extmode-high");
> > +
> > + if (device_property_read_u32(dev, "sharp,frames-per-sec", &fps))
> > + fps = 10;
> > +
> > + smemlcd_defio = devm_kzalloc(dev, sizeof(struct fb_deferred_io), GFP_KERNEL);
> > + if (!smemlcd_defio) {
> > + dev_err(dev, "Couldn't allocate deferred io.\n");
> > + ret = -ENOMEM;
> > + goto free_vmem;
> > + }
> > +
> > + smemlcd_defio->delay = HZ / fps;
> > + smemlcd_defio->deferred_io = &smemlcd_deferred_io;
> > +
> > + info->var = smemlcd_var;
> > + info->var.xres = devinfo->width;
> > + info->var.xres_virtual = devinfo->width;
> > + info->var.yres = devinfo->height;
> > + info->var.yres_virtual = devinfo->height;
> > +
> > + info->screen_buffer = vmem;
> > + info->screen_size = vmem_size;
> > +
> > + info->fbops = &smemlcd_ops;
> > + info->fix = smemlcd_fix;
> > + info->fix.line_length = par->vmem_width;
> > + info->fix.smem_start = __pa(vmem);
> > + info->fix.smem_len = vmem_size;
> > +
> > + info->fbdefio = smemlcd_defio;
> > +
> > + fb_deferred_io_init(info);
> > +
> > + spi_set_drvdata(spi, par);
> > +
> > + if (par->extmode) {
> > + par->extcomin_pwm = pwm_get(dev, NULL);
> > + if (IS_ERR(par->extcomin_pwm)) {
> > + ret = PTR_ERR(par->extcomin_pwm);
> > + dev_warn(dev, "Failed to get EXTCOMIN pwm: %d\n", ret);
> > + par->extcomin_pwm = NULL;
> > + } else {
> > +
> > + pwm_init_state(par->extcomin_pwm, &state);
> > +
> > + if (!state.period)
> > + state.period = NSEC_PER_SEC/fps;
> > +
> > + state.enabled = true;
> > + state.duty_cycle = state.period/2;
> > +
> > + ret = pwm_apply_state(par->extcomin_pwm, &state);
> > + if (ret)
> > + dev_warn(dev, "failed to apply EXTCOMIN pwm state: %d\n", ret);
> > + }
> > + } else {
> > + par->extcomin_pwm = NULL;
> > + }
> > +
> > + if (par->disp)
> > + gpiod_set_value_cansleep(par->disp, 1);
> > +
> > + /* spi test by clearing the display */
> > + par->spi_buf[0] = SMEMLCD_ALL_CLEAR;
> > + par->spi_buf[1] = SMEMLCD_DUMMY_DATA;
> > + ret = spi_write(spi, &(par->spi_buf[0]), 2);
> > + if (ret < 0){
> > + dev_err(dev, "Couldn't send SPI command\n");
> > + goto disable_hw;
> > + }
> > +
> > + ret = register_framebuffer(info);
> > + if (ret < 0) {
> > + dev_err(dev, "Failed to register framebuffer\n");
> > + goto disable_hw;
> > + }
> > +
> > + dev_info(dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
> > +
> > + if (!par->extmode)
> > + schedule_delayed_work(&par->d_work, smemlcd_defio->delay);
> > +
> > + return 0;
> > +
> > +disable_hw:
> > + if (par->disp)
> > + gpiod_set_value_cansleep(par->disp, 0);
> > + if (par->extcomin_pwm) {
> > + pwm_disable(par->extcomin_pwm);
> > + pwm_put(par->extcomin_pwm);
> > + }
> > + fb_deferred_io_cleanup(info);
> > +free_vmem:
> > + kfree(vmem);
> > +free_spi_buf:
> > + kfree(par->spi_buf);
> > +free_fb:
> > + cancel_delayed_work_sync(&par->d_work);
> > + mutex_destroy(&par->update_lock);
> > + framebuffer_release(info);
> > +
> > + return ret;
> > +}
> > +
> > +static int smemlcd_remove(struct spi_device *spi)
> > +{
> > + struct smemlcd_par *par = dev_get_drvdata(&spi->dev);
> > + struct fb_info *info = par->info;
> > +
> > + cancel_delayed_work_sync(&par->d_work);
> > +
> > + fb_deferred_io_cleanup(info);
> > +
> > + unregister_framebuffer(info);
> > +
> > + if (par->disp)
> > + gpiod_set_value_cansleep(par->disp, 0);
> > +
> > + if (par->extcomin_pwm) {
> > + pwm_disable(par->extcomin_pwm);
> > + pwm_put(par->extcomin_pwm);
> > + }
> > +
> > + fb_deferred_io_cleanup(info);
> > + __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
> > +
> > + kfree(par->spi_buf);
> > +
> > + mutex_destroy(&par->update_lock);
> > +
> > + framebuffer_release(info);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct spi_device_id smemlcd_spi_id[] = {
> > + {"ls027b7dh01", 0},
> > + {"ls044q7dh01", 0},
> > + {"ls013b7dh05", 0},
> > + {"ls013b7dh03", 0},
> > + {}
> > +};
> > +MODULE_DEVICE_TABLE(spi, smemlcd_spi_id);
> > +
> > +static struct spi_driver smemlcd_driver = {
> > + .probe = smemlcd_probe,
> > + .remove = smemlcd_remove,
> > + .id_table = smemlcd_spi_id,
> > + .driver = {
> > + .name = "smemlcdfb",
> > + .of_match_table = smemlcd_of_match,
> > + },
> > +};
> > +module_spi_driver(smemlcd_driver);
> > +
> > +MODULE_AUTHOR("Rodrigo Alencar <455.rodrigo.alencar@gmail.com>");
> > +MODULE_DESCRIPTION("Sharp Memory LCD Linux Framebuffer Driver");
> > +MODULE_LICENSE("GPL");
> > --
> > 2.23.0.rc1
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] video: fbdev: added driver for sharp memory lcd displays
2020-07-24 20:34 Rodrigo Alencar
@ 2020-07-26 17:22 ` Sam Ravnborg
2020-07-27 9:22 ` Daniel Vetter
0 siblings, 1 reply; 6+ messages in thread
From: Sam Ravnborg @ 2020-07-26 17:22 UTC (permalink / raw)
To: Rodrigo Alencar; +Cc: linux-fbdev, devicetree, linux-kernel, dri-devel
Hi Rodrigo.
On Fri, Jul 24, 2020 at 05:34:04PM -0300, Rodrigo Alencar wrote:
> The supported displays are ls027b7dh01 (tested), ls044q7dh01,
> ls013b7dh05, ls013b7dh03
>
> Signed-off-by: Rodrigo Alencar <455.rodrigo.alencar@gmail.com>
Thanks for submitting this driver.
Unfortunately I have some bad news for you...
The binding needs to be submitted as a seperate patch, and all new
bindings must use the DT schema format (.yaml files).
See Documentation/devicetree/bindings/*.rst for more info.
And on top of this fbdev/ is in maintenance mode so no new drivers.
New display drivers and panel drivers must go to gpu/drm/
and use the frameworks available there.
It looks like what we have here is a panel driver.
Look for other similar sharp panels in drm/panel/ to get an idea how to
add support for these new panels.
Soory, but looks forward for an updated patch.
Sam
> ---
> .../devicetree/bindings/display/smemlcdfb.txt | 46 ++
> drivers/video/fbdev/Kconfig | 14 +
> drivers/video/fbdev/Makefile | 1 +
> drivers/video/fbdev/smemlcdfb.c | 485 ++++++++++++++++++
> 4 files changed, 546 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/display/smemlcdfb.txt
> create mode 100644 drivers/video/fbdev/smemlcdfb.c
>
> diff --git a/Documentation/devicetree/bindings/display/smemlcdfb.txt b/Documentation/devicetree/bindings/display/smemlcdfb.txt
> new file mode 100644
> index 000000000000..e33025dd3374
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/smemlcdfb.txt
> @@ -0,0 +1,46 @@
> +Sharp Memory LCD Linux Framebuffer Driver
> +
> +Required properties:
> + - compatible: It should be "sharp,<model-no>". The supported displays are
> + ls027b7dh01, ls044q7dh01, ls013b7dh05, ls013b7dh03. Maybe other
> + monochromatic models can be supported with the current code.
> + - reg: SPI chip select number for the device.
> + - spi-max-frequency: Max SPI frequency to use. One must verify the datasheet.
> + - spi-cs-high: Sharp Memory LCD needs chipselect high.
> +
> +Optional properties:
> + - sharp,frames-per-sec: It should contain the desired frames per second.
> + It does not represent the actual frame rate. Default: 10
> + - sharp,extmode-high: External COM Input signal is expected in EXTCOMIN port.
> + This is recommended to reduce CPU and SPI Load.
> + - pwm: If property "sharp,extmode-high" is specified, this is recommended.
> + It should contain the pwm to use, according to
> + Documentation/devicetree/bindings/pwm/pwm.txt
> + Verify the display datasheet for the EXTCOMIN signal period
> + - disp-gpios: The GPIO used to enable the display, if available. See
> + Documentation/devicetree/bindings/gpio/gpio.txt for details.
> +
> +Examples:
> +
> +ls027b7dh01: smemlcd@0 {
> + compatible = "sharp,ls027b7dh01";
> + reg = <0>;
> + spi-max-frequency = <1000000>;
> + spi-cs-high;
> + disp-gpios = <&gpio0 7>;
> + disp-active-high;
> + sharp,extmode-high;
> + pwms = <&pwm 0 100000000>;
> +};
> +
> +ls013b7dh05: smemlcd@3 {
> + compatible = "sharp,ls013b7dh05";
> + reg = <3>;
> + spi-max-frequency = <1000000>;
> + spi-cs-high;
> + disp-gpios = <&gpio0 13>;
> + disp-active-high;
> + sharp,extmode-high;
> + pwms = <&pwm 0 50000000>;
> + sharp,frames-per-sec = <20>;
> +};
> diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
> index b2c9dd4f0cb5..0fff47a59d8b 100644
> --- a/drivers/video/fbdev/Kconfig
> +++ b/drivers/video/fbdev/Kconfig
> @@ -2221,6 +2221,20 @@ config FB_SSD1307
> This driver implements support for the Solomon SSD1307
> OLED controller over I2C.
>
> +config FB_SMEMLCD
> + tristate "Sharp Memory LCD framebuffer support"
> + depends on FB && SPI
> + depends on OF
> + depends on GPIOLIB || COMPILE_TEST
> + select FB_SYS_FOPS
> + select FB_SYS_FILLRECT
> + select FB_SYS_COPYAREA
> + select FB_SYS_IMAGEBLIT
> + select FB_DEFERRED_IO
> + select PWM
> + help
> + This driver implements support for the Sharp Memory LCD
> +
> config FB_SM712
> tristate "Silicon Motion SM712 framebuffer support"
> depends on FB && PCI
> diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
> index cad4fb64442a..5c58dfd8ac08 100644
> --- a/drivers/video/fbdev/Makefile
> +++ b/drivers/video/fbdev/Makefile
> @@ -128,6 +128,7 @@ obj-$(CONFIG_FB_OF) += offb.o
> obj-$(CONFIG_FB_MX3) += mx3fb.o
> obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
> obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o
> +obj-$(CONFIG_FB_SMEMLCD) += smemlcdfb.o
> obj-$(CONFIG_FB_SIMPLE) += simplefb.o
>
> # the test framebuffer is last
> diff --git a/drivers/video/fbdev/smemlcdfb.c b/drivers/video/fbdev/smemlcdfb.c
> new file mode 100644
> index 000000000000..54a0c57b6713
> --- /dev/null
> +++ b/drivers/video/fbdev/smemlcdfb.c
> @@ -0,0 +1,485 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2019 IMBEL http://www.imbel.gov.br
> + * Rodrigo Alencar <alencar.fmce@imbel.gov.br>
> + */
> +
> +#include <linux/fb.h>
> +#include <linux/bitrev.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/spi/spi.h>
> +#include <linux/module.h>
> +#include <linux/property.h>
> +#include <linux/pwm.h>
> +
> +#define SMEMLCD_DATA_UPDATE 0x80
> +#define SMEMLCD_FRAME_INVERSION 0x40
> +#define SMEMLCD_ALL_CLEAR 0x20
> +#define SMEMLCD_DUMMY_DATA 0x00
> +
> +struct smemlcd_info {
> + u32 width;
> + u32 height;
> +};
> +
> +struct smemlcd_par {
> + struct spi_device *spi;
> + struct fb_info *info;
> + struct pwm_device *extcomin_pwm;
> + struct gpio_desc *disp;
> + struct delayed_work d_work;
> + struct mutex update_lock;
> +
> + u8 *spi_buf;
> +
> + bool extmode;
> + u32 spi_width;
> + u32 vmem_width;
> +
> + u8 vcom;
> + u32 start;
> + u32 height;
> +};
> +
> +static const struct fb_fix_screeninfo smemlcd_fix = {
> + .id = "Sharp Memory LCD",
> + .type = FB_TYPE_PACKED_PIXELS,
> + .visual = FB_VISUAL_MONO10,
> + .xpanstep = 0,
> + .ypanstep = 0,
> + .ywrapstep = 0,
> + .accel = FB_ACCEL_NONE,
> +};
> +
> +static const struct fb_var_screeninfo smemlcd_var = {
> + .bits_per_pixel = 1,
> + .red = { .length = 1 },
> + .green = { .length = 1 },
> + .blue = { .length = 1 },
> +};
> +
> +static void smemlcd_update(struct smemlcd_par *par)
> +{
> + struct spi_device *spi = par->spi;
> + u8 *vmem = par->info->screen_buffer;
> + u8 *buf_ptr = par->spi_buf;
> + int ret;
> + u32 i,j;
> +
> + if (par->start + par->height > par->info->var.yres) {
> + par->start = 0;
> + par->height = 0;
> + }
> + /* go to start line */
> + vmem += par->start * par->vmem_width;
> + /* update vcom */
> + par->vcom ^= SMEMLCD_FRAME_INVERSION;
> + /* mode selection */
> + *(buf_ptr++) = (par->height)? (SMEMLCD_DATA_UPDATE | par->vcom) : par->vcom;
> +
> + /* not all SPI masters have LSB-first mode, bitrev8 is used */
> + for (i = par->start + 1; i < par->start + par->height + 1; i++) {
> + /* gate line address */
> + *(buf_ptr++) = bitrev8(i);
> + /* data writing */
> + for (j = 0; j < par->spi_width; j++)
> + *(buf_ptr++) = bitrev8(*(vmem++));
> + /* dummy data */
> + *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
> + /* video memory alignment */
> + for (; j < par->vmem_width; j++)
> + vmem++;
> + }
> + /* dummy data */
> + *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
> +
> + ret = spi_write(spi, &(par->spi_buf[0]), par->height * (par->spi_width + 2) + 2);
> + if (ret < 0)
> + dev_err(&spi->dev, "Couldn't send SPI command.\n");
> +
> + par->start = U32_MAX;
> + par->height = 0;
> +}
> +
> +static void smemlcd_frame(struct smemlcd_par *par, u32 req_start, u32 req_height)
> +{
> + u32 end = par->start + par->height;
> + u32 req_end = req_start + req_height;
> + if (req_end > par->info->var.yres)
> + req_end = par->info->var.yres;
> + if (par->start > req_start)
> + par->start = req_start;
> + if (end < req_end || end > par->info->var.yres)
> + end = req_end;
> + par->height = end - par->start;
> +}
> +
> +static void smemlcd_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
> +{
> + struct smemlcd_par *par = info->par;
> + sys_fillrect(info, rect);
> +
> + mutex_lock(&par->update_lock);
> + smemlcd_frame(par, rect->dy, rect->height);
> + if(par->extmode)
> + smemlcd_update(par);
> + mutex_unlock(&par->update_lock);
> +}
> +
> +static void smemlcd_imageblit(struct fb_info *info, const struct fb_image *image)
> +{
> + struct smemlcd_par *par = info->par;
> + sys_imageblit(info, image);
> +
> + mutex_lock(&par->update_lock);
> + smemlcd_frame(par, image->dy, image->height);
> + if(par->extmode)
> + smemlcd_update(par);
> + mutex_unlock(&par->update_lock);
> +}
> +
> +static void smemlcd_copyarea(struct fb_info *info, const struct fb_copyarea *area)
> +{
> + struct smemlcd_par *par = info->par;
> + sys_copyarea(info, area);
> +
> + mutex_lock(&par->update_lock);
> + smemlcd_frame(par, area->dy, area->height);
> + if(par->extmode)
> + smemlcd_update(par);
> + mutex_unlock(&par->update_lock);
> +}
> +
> +static ssize_t smemlcd_write(struct fb_info *info, const char __user * buf, size_t count, loff_t * ppos)
> +{
> + ssize_t ret;
> + struct smemlcd_par *par = info->par;
> + u32 req_start, req_height;
> + u32 offset = (u32) * ppos;
> +
> + ret = fb_sys_write(info, buf, count, ppos);
> + if (ret > 0) {
> + mutex_lock(&par->update_lock);
> + req_start = max((int)(offset / par->vmem_width), 0);
> + req_height = ret / par->vmem_width + 1;
> + smemlcd_frame(par, req_start, req_height);
> + if(par->extmode)
> + smemlcd_update(par);
> + mutex_unlock(&par->update_lock);
> + }
> +
> + return ret;
> +}
> +
> +static int smemlcd_blank(int blank_mode, struct fb_info *info)
> +{
> + struct smemlcd_par *par = info->par;
> +
> + if (par->disp) {
> + if (blank_mode != FB_BLANK_UNBLANK)
> + gpiod_set_value_cansleep(par->disp, 0);
> + else
> + gpiod_set_value_cansleep(par->disp, 1);
> + }
> +
> + return 0;
> +}
> +
> +static void smemlcd_deferred_io(struct fb_info *info, struct list_head *pagelist)
> +{
> + struct smemlcd_par *par = info->par;
> +
> + mutex_lock(&par->update_lock);
> +
> + if (!list_empty(pagelist)) {
> + struct page *cur;
> + u32 req_start;
> + u32 req_height = (PAGE_SIZE / par->vmem_width) + 1;
> +
> + list_for_each_entry(cur, pagelist, lru) {
> + req_start = (cur->index << PAGE_SHIFT) / par->vmem_width;
> + smemlcd_frame(par, req_start, req_height);
> + }
> + }
> +
> + if(par->extmode)
> + smemlcd_update(par);
> + mutex_unlock(&par->update_lock);
> +}
> +
> +static void smemlcd_update_work(struct work_struct *work)
> +{
> + struct smemlcd_par *par = container_of(work, struct smemlcd_par, d_work.work);
> + struct fb_info *info = par->info;
> +
> + mutex_lock(&par->update_lock);
> + smemlcd_update(par);
> + mutex_unlock(&par->update_lock);
> +
> + if (!par->extmode)
> + schedule_delayed_work(&par->d_work, info->fbdefio->delay);
> +}
> +
> +static const struct fb_ops smemlcd_ops = {
> + .owner = THIS_MODULE,
> + .fb_read = fb_sys_read,
> + .fb_write = smemlcd_write,
> + .fb_fillrect = smemlcd_fillrect,
> + .fb_copyarea = smemlcd_copyarea,
> + .fb_imageblit = smemlcd_imageblit,
> + .fb_blank = smemlcd_blank,
> +};
> +
> +static struct smemlcd_info ls027b7dh01_info = {
> + .width = 400,
> + .height = 240,
> +};
> +
> +static struct smemlcd_info ls044q7dh01_info = {
> + .width = 320,
> + .height = 240,
> +};
> +
> +static struct smemlcd_info ls013b7dh05_info = {
> + .width = 144,
> + .height = 168,
> +};
> +
> +static struct smemlcd_info ls013b7dh03_info = {
> + .width = 128,
> + .height = 128,
> +};
> +
> +static const struct of_device_id smemlcd_of_match[] = {
> + {
> + .compatible = "sharp,ls027b7dh01",
> + .data = (void *)&ls027b7dh01_info,
> + },
> + {
> + .compatible = "sharp,ls044q7dh01",
> + .data = (void *)&ls044q7dh01_info,
> + },
> + {
> + .compatible = "sharp,ls013b7dh05",
> + .data = (void *)&ls013b7dh05_info,
> + },
> + {
> + .compatible = "sharp,ls013b7dh03",
> + .data = (void *)&ls013b7dh03_info,
> + },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, smemlcd_of_match);
> +
> +static int smemlcd_probe(struct spi_device *spi)
> +{
> + struct device *dev = &spi->dev;
> + struct fb_info *info;
> + struct smemlcd_par *par;
> + const struct smemlcd_info *devinfo;
> + struct fb_deferred_io *smemlcd_defio;
> + struct pwm_state state;
> + u32 vmem_size, fps;
> + void *vmem;
> + int ret;
> +
> + info = framebuffer_alloc(sizeof(struct smemlcd_par), dev);
> + if (!info)
> + return -ENOMEM;
> +
> + par = info->par;
> + par->info = info;
> + par->spi = spi;
> +
> + devinfo = device_get_match_data(dev);
> +
> + mutex_init(&par->update_lock);
> + INIT_DELAYED_WORK(&par->d_work, smemlcd_update_work);
> + par->spi_width = devinfo->width / 8;
> + par->vmem_width = ((devinfo->width + 31) & ~31) >> 3;
> + par->vcom = 0;
> + par->start = 0;
> + par->height = 0;
> +
> + par->disp = devm_gpiod_get_optional(dev, "disp", GPIOD_OUT_LOW);
> + if (IS_ERR(par->disp)) {
> + ret = PTR_ERR(par->disp);
> + dev_err(dev, "Failed to get DISP gpio: %d\n", ret);
> + goto free_fb;
> + }
> +
> + par->spi_buf = kzalloc(devinfo->height * (par->spi_width + 2) + 2, GFP_KERNEL);
> + if (!par->spi_buf) {
> + ret = -ENOMEM;
> + dev_err(dev, "Failed to allocate data for spi transfers.\n");
> + goto free_fb;
> + }
> +
> + vmem_size = par->vmem_width * devinfo->height;
> +
> + vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, get_order(vmem_size));
> + if (!vmem) {
> + ret = -ENOMEM;
> + dev_err(dev, "Failed to allocate video memory.\n");
> + goto free_spi_buf;
> + }
> +
> + par->extmode = device_property_read_bool(dev, "sharp,extmode-high");
> +
> + if (device_property_read_u32(dev, "sharp,frames-per-sec", &fps))
> + fps = 10;
> +
> + smemlcd_defio = devm_kzalloc(dev, sizeof(struct fb_deferred_io), GFP_KERNEL);
> + if (!smemlcd_defio) {
> + dev_err(dev, "Couldn't allocate deferred io.\n");
> + ret = -ENOMEM;
> + goto free_vmem;
> + }
> +
> + smemlcd_defio->delay = HZ / fps;
> + smemlcd_defio->deferred_io = &smemlcd_deferred_io;
> +
> + info->var = smemlcd_var;
> + info->var.xres = devinfo->width;
> + info->var.xres_virtual = devinfo->width;
> + info->var.yres = devinfo->height;
> + info->var.yres_virtual = devinfo->height;
> +
> + info->screen_buffer = vmem;
> + info->screen_size = vmem_size;
> +
> + info->fbops = &smemlcd_ops;
> + info->fix = smemlcd_fix;
> + info->fix.line_length = par->vmem_width;
> + info->fix.smem_start = __pa(vmem);
> + info->fix.smem_len = vmem_size;
> +
> + info->fbdefio = smemlcd_defio;
> +
> + fb_deferred_io_init(info);
> +
> + spi_set_drvdata(spi, par);
> +
> + if (par->extmode) {
> + par->extcomin_pwm = pwm_get(dev, NULL);
> + if (IS_ERR(par->extcomin_pwm)) {
> + ret = PTR_ERR(par->extcomin_pwm);
> + dev_warn(dev, "Failed to get EXTCOMIN pwm: %d\n", ret);
> + par->extcomin_pwm = NULL;
> + } else {
> +
> + pwm_init_state(par->extcomin_pwm, &state);
> +
> + if (!state.period)
> + state.period = NSEC_PER_SEC/fps;
> +
> + state.enabled = true;
> + state.duty_cycle = state.period/2;
> +
> + ret = pwm_apply_state(par->extcomin_pwm, &state);
> + if (ret)
> + dev_warn(dev, "failed to apply EXTCOMIN pwm state: %d\n", ret);
> + }
> + } else {
> + par->extcomin_pwm = NULL;
> + }
> +
> + if (par->disp)
> + gpiod_set_value_cansleep(par->disp, 1);
> +
> + /* spi test by clearing the display */
> + par->spi_buf[0] = SMEMLCD_ALL_CLEAR;
> + par->spi_buf[1] = SMEMLCD_DUMMY_DATA;
> + ret = spi_write(spi, &(par->spi_buf[0]), 2);
> + if (ret < 0){
> + dev_err(dev, "Couldn't send SPI command\n");
> + goto disable_hw;
> + }
> +
> + ret = register_framebuffer(info);
> + if (ret < 0) {
> + dev_err(dev, "Failed to register framebuffer\n");
> + goto disable_hw;
> + }
> +
> + dev_info(dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
> +
> + if (!par->extmode)
> + schedule_delayed_work(&par->d_work, smemlcd_defio->delay);
> +
> + return 0;
> +
> +disable_hw:
> + if (par->disp)
> + gpiod_set_value_cansleep(par->disp, 0);
> + if (par->extcomin_pwm) {
> + pwm_disable(par->extcomin_pwm);
> + pwm_put(par->extcomin_pwm);
> + }
> + fb_deferred_io_cleanup(info);
> +free_vmem:
> + kfree(vmem);
> +free_spi_buf:
> + kfree(par->spi_buf);
> +free_fb:
> + cancel_delayed_work_sync(&par->d_work);
> + mutex_destroy(&par->update_lock);
> + framebuffer_release(info);
> +
> + return ret;
> +}
> +
> +static int smemlcd_remove(struct spi_device *spi)
> +{
> + struct smemlcd_par *par = dev_get_drvdata(&spi->dev);
> + struct fb_info *info = par->info;
> +
> + cancel_delayed_work_sync(&par->d_work);
> +
> + fb_deferred_io_cleanup(info);
> +
> + unregister_framebuffer(info);
> +
> + if (par->disp)
> + gpiod_set_value_cansleep(par->disp, 0);
> +
> + if (par->extcomin_pwm) {
> + pwm_disable(par->extcomin_pwm);
> + pwm_put(par->extcomin_pwm);
> + }
> +
> + fb_deferred_io_cleanup(info);
> + __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
> +
> + kfree(par->spi_buf);
> +
> + mutex_destroy(&par->update_lock);
> +
> + framebuffer_release(info);
> +
> + return 0;
> +}
> +
> +static const struct spi_device_id smemlcd_spi_id[] = {
> + {"ls027b7dh01", 0},
> + {"ls044q7dh01", 0},
> + {"ls013b7dh05", 0},
> + {"ls013b7dh03", 0},
> + {}
> +};
> +MODULE_DEVICE_TABLE(spi, smemlcd_spi_id);
> +
> +static struct spi_driver smemlcd_driver = {
> + .probe = smemlcd_probe,
> + .remove = smemlcd_remove,
> + .id_table = smemlcd_spi_id,
> + .driver = {
> + .name = "smemlcdfb",
> + .of_match_table = smemlcd_of_match,
> + },
> +};
> +module_spi_driver(smemlcd_driver);
> +
> +MODULE_AUTHOR("Rodrigo Alencar <455.rodrigo.alencar@gmail.com>");
> +MODULE_DESCRIPTION("Sharp Memory LCD Linux Framebuffer Driver");
> +MODULE_LICENSE("GPL");
> --
> 2.23.0.rc1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] video: fbdev: added driver for sharp memory lcd displays
@ 2020-07-24 20:34 Rodrigo Alencar
2020-07-26 17:22 ` Sam Ravnborg
0 siblings, 1 reply; 6+ messages in thread
From: Rodrigo Alencar @ 2020-07-24 20:34 UTC (permalink / raw)
To: linux-fbdev; +Cc: linux-kernel, dri-devel, devicetree, Rodrigo Alencar
The supported displays are ls027b7dh01 (tested), ls044q7dh01,
ls013b7dh05, ls013b7dh03
Signed-off-by: Rodrigo Alencar <455.rodrigo.alencar@gmail.com>
---
.../devicetree/bindings/display/smemlcdfb.txt | 46 ++
drivers/video/fbdev/Kconfig | 14 +
drivers/video/fbdev/Makefile | 1 +
drivers/video/fbdev/smemlcdfb.c | 485 ++++++++++++++++++
4 files changed, 546 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/smemlcdfb.txt
create mode 100644 drivers/video/fbdev/smemlcdfb.c
diff --git a/Documentation/devicetree/bindings/display/smemlcdfb.txt b/Documentation/devicetree/bindings/display/smemlcdfb.txt
new file mode 100644
index 000000000000..e33025dd3374
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/smemlcdfb.txt
@@ -0,0 +1,46 @@
+Sharp Memory LCD Linux Framebuffer Driver
+
+Required properties:
+ - compatible: It should be "sharp,<model-no>". The supported displays are
+ ls027b7dh01, ls044q7dh01, ls013b7dh05, ls013b7dh03. Maybe other
+ monochromatic models can be supported with the current code.
+ - reg: SPI chip select number for the device.
+ - spi-max-frequency: Max SPI frequency to use. One must verify the datasheet.
+ - spi-cs-high: Sharp Memory LCD needs chipselect high.
+
+Optional properties:
+ - sharp,frames-per-sec: It should contain the desired frames per second.
+ It does not represent the actual frame rate. Default: 10
+ - sharp,extmode-high: External COM Input signal is expected in EXTCOMIN port.
+ This is recommended to reduce CPU and SPI Load.
+ - pwm: If property "sharp,extmode-high" is specified, this is recommended.
+ It should contain the pwm to use, according to
+ Documentation/devicetree/bindings/pwm/pwm.txt
+ Verify the display datasheet for the EXTCOMIN signal period
+ - disp-gpios: The GPIO used to enable the display, if available. See
+ Documentation/devicetree/bindings/gpio/gpio.txt for details.
+
+Examples:
+
+ls027b7dh01: smemlcd@0 {
+ compatible = "sharp,ls027b7dh01";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ spi-cs-high;
+ disp-gpios = <&gpio0 7>;
+ disp-active-high;
+ sharp,extmode-high;
+ pwms = <&pwm 0 100000000>;
+};
+
+ls013b7dh05: smemlcd@3 {
+ compatible = "sharp,ls013b7dh05";
+ reg = <3>;
+ spi-max-frequency = <1000000>;
+ spi-cs-high;
+ disp-gpios = <&gpio0 13>;
+ disp-active-high;
+ sharp,extmode-high;
+ pwms = <&pwm 0 50000000>;
+ sharp,frames-per-sec = <20>;
+};
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index b2c9dd4f0cb5..0fff47a59d8b 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2221,6 +2221,20 @@ config FB_SSD1307
This driver implements support for the Solomon SSD1307
OLED controller over I2C.
+config FB_SMEMLCD
+ tristate "Sharp Memory LCD framebuffer support"
+ depends on FB && SPI
+ depends on OF
+ depends on GPIOLIB || COMPILE_TEST
+ select FB_SYS_FOPS
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_DEFERRED_IO
+ select PWM
+ help
+ This driver implements support for the Sharp Memory LCD
+
config FB_SM712
tristate "Silicon Motion SM712 framebuffer support"
depends on FB && PCI
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index cad4fb64442a..5c58dfd8ac08 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -128,6 +128,7 @@ obj-$(CONFIG_FB_OF) += offb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o
+obj-$(CONFIG_FB_SMEMLCD) += smemlcdfb.o
obj-$(CONFIG_FB_SIMPLE) += simplefb.o
# the test framebuffer is last
diff --git a/drivers/video/fbdev/smemlcdfb.c b/drivers/video/fbdev/smemlcdfb.c
new file mode 100644
index 000000000000..54a0c57b6713
--- /dev/null
+++ b/drivers/video/fbdev/smemlcdfb.c
@@ -0,0 +1,485 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 IMBEL http://www.imbel.gov.br
+ * Rodrigo Alencar <alencar.fmce@imbel.gov.br>
+ */
+
+#include <linux/fb.h>
+#include <linux/bitrev.h>
+#include <linux/gpio/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/pwm.h>
+
+#define SMEMLCD_DATA_UPDATE 0x80
+#define SMEMLCD_FRAME_INVERSION 0x40
+#define SMEMLCD_ALL_CLEAR 0x20
+#define SMEMLCD_DUMMY_DATA 0x00
+
+struct smemlcd_info {
+ u32 width;
+ u32 height;
+};
+
+struct smemlcd_par {
+ struct spi_device *spi;
+ struct fb_info *info;
+ struct pwm_device *extcomin_pwm;
+ struct gpio_desc *disp;
+ struct delayed_work d_work;
+ struct mutex update_lock;
+
+ u8 *spi_buf;
+
+ bool extmode;
+ u32 spi_width;
+ u32 vmem_width;
+
+ u8 vcom;
+ u32 start;
+ u32 height;
+};
+
+static const struct fb_fix_screeninfo smemlcd_fix = {
+ .id = "Sharp Memory LCD",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_MONO10,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+ .accel = FB_ACCEL_NONE,
+};
+
+static const struct fb_var_screeninfo smemlcd_var = {
+ .bits_per_pixel = 1,
+ .red = { .length = 1 },
+ .green = { .length = 1 },
+ .blue = { .length = 1 },
+};
+
+static void smemlcd_update(struct smemlcd_par *par)
+{
+ struct spi_device *spi = par->spi;
+ u8 *vmem = par->info->screen_buffer;
+ u8 *buf_ptr = par->spi_buf;
+ int ret;
+ u32 i,j;
+
+ if (par->start + par->height > par->info->var.yres) {
+ par->start = 0;
+ par->height = 0;
+ }
+ /* go to start line */
+ vmem += par->start * par->vmem_width;
+ /* update vcom */
+ par->vcom ^= SMEMLCD_FRAME_INVERSION;
+ /* mode selection */
+ *(buf_ptr++) = (par->height)? (SMEMLCD_DATA_UPDATE | par->vcom) : par->vcom;
+
+ /* not all SPI masters have LSB-first mode, bitrev8 is used */
+ for (i = par->start + 1; i < par->start + par->height + 1; i++) {
+ /* gate line address */
+ *(buf_ptr++) = bitrev8(i);
+ /* data writing */
+ for (j = 0; j < par->spi_width; j++)
+ *(buf_ptr++) = bitrev8(*(vmem++));
+ /* dummy data */
+ *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
+ /* video memory alignment */
+ for (; j < par->vmem_width; j++)
+ vmem++;
+ }
+ /* dummy data */
+ *(buf_ptr++) = SMEMLCD_DUMMY_DATA;
+
+ ret = spi_write(spi, &(par->spi_buf[0]), par->height * (par->spi_width + 2) + 2);
+ if (ret < 0)
+ dev_err(&spi->dev, "Couldn't send SPI command.\n");
+
+ par->start = U32_MAX;
+ par->height = 0;
+}
+
+static void smemlcd_frame(struct smemlcd_par *par, u32 req_start, u32 req_height)
+{
+ u32 end = par->start + par->height;
+ u32 req_end = req_start + req_height;
+ if (req_end > par->info->var.yres)
+ req_end = par->info->var.yres;
+ if (par->start > req_start)
+ par->start = req_start;
+ if (end < req_end || end > par->info->var.yres)
+ end = req_end;
+ par->height = end - par->start;
+}
+
+static void smemlcd_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct smemlcd_par *par = info->par;
+ sys_fillrect(info, rect);
+
+ mutex_lock(&par->update_lock);
+ smemlcd_frame(par, rect->dy, rect->height);
+ if(par->extmode)
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+}
+
+static void smemlcd_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct smemlcd_par *par = info->par;
+ sys_imageblit(info, image);
+
+ mutex_lock(&par->update_lock);
+ smemlcd_frame(par, image->dy, image->height);
+ if(par->extmode)
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+}
+
+static void smemlcd_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct smemlcd_par *par = info->par;
+ sys_copyarea(info, area);
+
+ mutex_lock(&par->update_lock);
+ smemlcd_frame(par, area->dy, area->height);
+ if(par->extmode)
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+}
+
+static ssize_t smemlcd_write(struct fb_info *info, const char __user * buf, size_t count, loff_t * ppos)
+{
+ ssize_t ret;
+ struct smemlcd_par *par = info->par;
+ u32 req_start, req_height;
+ u32 offset = (u32) * ppos;
+
+ ret = fb_sys_write(info, buf, count, ppos);
+ if (ret > 0) {
+ mutex_lock(&par->update_lock);
+ req_start = max((int)(offset / par->vmem_width), 0);
+ req_height = ret / par->vmem_width + 1;
+ smemlcd_frame(par, req_start, req_height);
+ if(par->extmode)
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+ }
+
+ return ret;
+}
+
+static int smemlcd_blank(int blank_mode, struct fb_info *info)
+{
+ struct smemlcd_par *par = info->par;
+
+ if (par->disp) {
+ if (blank_mode != FB_BLANK_UNBLANK)
+ gpiod_set_value_cansleep(par->disp, 0);
+ else
+ gpiod_set_value_cansleep(par->disp, 1);
+ }
+
+ return 0;
+}
+
+static void smemlcd_deferred_io(struct fb_info *info, struct list_head *pagelist)
+{
+ struct smemlcd_par *par = info->par;
+
+ mutex_lock(&par->update_lock);
+
+ if (!list_empty(pagelist)) {
+ struct page *cur;
+ u32 req_start;
+ u32 req_height = (PAGE_SIZE / par->vmem_width) + 1;
+
+ list_for_each_entry(cur, pagelist, lru) {
+ req_start = (cur->index << PAGE_SHIFT) / par->vmem_width;
+ smemlcd_frame(par, req_start, req_height);
+ }
+ }
+
+ if(par->extmode)
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+}
+
+static void smemlcd_update_work(struct work_struct *work)
+{
+ struct smemlcd_par *par = container_of(work, struct smemlcd_par, d_work.work);
+ struct fb_info *info = par->info;
+
+ mutex_lock(&par->update_lock);
+ smemlcd_update(par);
+ mutex_unlock(&par->update_lock);
+
+ if (!par->extmode)
+ schedule_delayed_work(&par->d_work, info->fbdefio->delay);
+}
+
+static const struct fb_ops smemlcd_ops = {
+ .owner = THIS_MODULE,
+ .fb_read = fb_sys_read,
+ .fb_write = smemlcd_write,
+ .fb_fillrect = smemlcd_fillrect,
+ .fb_copyarea = smemlcd_copyarea,
+ .fb_imageblit = smemlcd_imageblit,
+ .fb_blank = smemlcd_blank,
+};
+
+static struct smemlcd_info ls027b7dh01_info = {
+ .width = 400,
+ .height = 240,
+};
+
+static struct smemlcd_info ls044q7dh01_info = {
+ .width = 320,
+ .height = 240,
+};
+
+static struct smemlcd_info ls013b7dh05_info = {
+ .width = 144,
+ .height = 168,
+};
+
+static struct smemlcd_info ls013b7dh03_info = {
+ .width = 128,
+ .height = 128,
+};
+
+static const struct of_device_id smemlcd_of_match[] = {
+ {
+ .compatible = "sharp,ls027b7dh01",
+ .data = (void *)&ls027b7dh01_info,
+ },
+ {
+ .compatible = "sharp,ls044q7dh01",
+ .data = (void *)&ls044q7dh01_info,
+ },
+ {
+ .compatible = "sharp,ls013b7dh05",
+ .data = (void *)&ls013b7dh05_info,
+ },
+ {
+ .compatible = "sharp,ls013b7dh03",
+ .data = (void *)&ls013b7dh03_info,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, smemlcd_of_match);
+
+static int smemlcd_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct fb_info *info;
+ struct smemlcd_par *par;
+ const struct smemlcd_info *devinfo;
+ struct fb_deferred_io *smemlcd_defio;
+ struct pwm_state state;
+ u32 vmem_size, fps;
+ void *vmem;
+ int ret;
+
+ info = framebuffer_alloc(sizeof(struct smemlcd_par), dev);
+ if (!info)
+ return -ENOMEM;
+
+ par = info->par;
+ par->info = info;
+ par->spi = spi;
+
+ devinfo = device_get_match_data(dev);
+
+ mutex_init(&par->update_lock);
+ INIT_DELAYED_WORK(&par->d_work, smemlcd_update_work);
+ par->spi_width = devinfo->width / 8;
+ par->vmem_width = ((devinfo->width + 31) & ~31) >> 3;
+ par->vcom = 0;
+ par->start = 0;
+ par->height = 0;
+
+ par->disp = devm_gpiod_get_optional(dev, "disp", GPIOD_OUT_LOW);
+ if (IS_ERR(par->disp)) {
+ ret = PTR_ERR(par->disp);
+ dev_err(dev, "Failed to get DISP gpio: %d\n", ret);
+ goto free_fb;
+ }
+
+ par->spi_buf = kzalloc(devinfo->height * (par->spi_width + 2) + 2, GFP_KERNEL);
+ if (!par->spi_buf) {
+ ret = -ENOMEM;
+ dev_err(dev, "Failed to allocate data for spi transfers.\n");
+ goto free_fb;
+ }
+
+ vmem_size = par->vmem_width * devinfo->height;
+
+ vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, get_order(vmem_size));
+ if (!vmem) {
+ ret = -ENOMEM;
+ dev_err(dev, "Failed to allocate video memory.\n");
+ goto free_spi_buf;
+ }
+
+ par->extmode = device_property_read_bool(dev, "sharp,extmode-high");
+
+ if (device_property_read_u32(dev, "sharp,frames-per-sec", &fps))
+ fps = 10;
+
+ smemlcd_defio = devm_kzalloc(dev, sizeof(struct fb_deferred_io), GFP_KERNEL);
+ if (!smemlcd_defio) {
+ dev_err(dev, "Couldn't allocate deferred io.\n");
+ ret = -ENOMEM;
+ goto free_vmem;
+ }
+
+ smemlcd_defio->delay = HZ / fps;
+ smemlcd_defio->deferred_io = &smemlcd_deferred_io;
+
+ info->var = smemlcd_var;
+ info->var.xres = devinfo->width;
+ info->var.xres_virtual = devinfo->width;
+ info->var.yres = devinfo->height;
+ info->var.yres_virtual = devinfo->height;
+
+ info->screen_buffer = vmem;
+ info->screen_size = vmem_size;
+
+ info->fbops = &smemlcd_ops;
+ info->fix = smemlcd_fix;
+ info->fix.line_length = par->vmem_width;
+ info->fix.smem_start = __pa(vmem);
+ info->fix.smem_len = vmem_size;
+
+ info->fbdefio = smemlcd_defio;
+
+ fb_deferred_io_init(info);
+
+ spi_set_drvdata(spi, par);
+
+ if (par->extmode) {
+ par->extcomin_pwm = pwm_get(dev, NULL);
+ if (IS_ERR(par->extcomin_pwm)) {
+ ret = PTR_ERR(par->extcomin_pwm);
+ dev_warn(dev, "Failed to get EXTCOMIN pwm: %d\n", ret);
+ par->extcomin_pwm = NULL;
+ } else {
+
+ pwm_init_state(par->extcomin_pwm, &state);
+
+ if (!state.period)
+ state.period = NSEC_PER_SEC/fps;
+
+ state.enabled = true;
+ state.duty_cycle = state.period/2;
+
+ ret = pwm_apply_state(par->extcomin_pwm, &state);
+ if (ret)
+ dev_warn(dev, "failed to apply EXTCOMIN pwm state: %d\n", ret);
+ }
+ } else {
+ par->extcomin_pwm = NULL;
+ }
+
+ if (par->disp)
+ gpiod_set_value_cansleep(par->disp, 1);
+
+ /* spi test by clearing the display */
+ par->spi_buf[0] = SMEMLCD_ALL_CLEAR;
+ par->spi_buf[1] = SMEMLCD_DUMMY_DATA;
+ ret = spi_write(spi, &(par->spi_buf[0]), 2);
+ if (ret < 0){
+ dev_err(dev, "Couldn't send SPI command\n");
+ goto disable_hw;
+ }
+
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register framebuffer\n");
+ goto disable_hw;
+ }
+
+ dev_info(dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
+
+ if (!par->extmode)
+ schedule_delayed_work(&par->d_work, smemlcd_defio->delay);
+
+ return 0;
+
+disable_hw:
+ if (par->disp)
+ gpiod_set_value_cansleep(par->disp, 0);
+ if (par->extcomin_pwm) {
+ pwm_disable(par->extcomin_pwm);
+ pwm_put(par->extcomin_pwm);
+ }
+ fb_deferred_io_cleanup(info);
+free_vmem:
+ kfree(vmem);
+free_spi_buf:
+ kfree(par->spi_buf);
+free_fb:
+ cancel_delayed_work_sync(&par->d_work);
+ mutex_destroy(&par->update_lock);
+ framebuffer_release(info);
+
+ return ret;
+}
+
+static int smemlcd_remove(struct spi_device *spi)
+{
+ struct smemlcd_par *par = dev_get_drvdata(&spi->dev);
+ struct fb_info *info = par->info;
+
+ cancel_delayed_work_sync(&par->d_work);
+
+ fb_deferred_io_cleanup(info);
+
+ unregister_framebuffer(info);
+
+ if (par->disp)
+ gpiod_set_value_cansleep(par->disp, 0);
+
+ if (par->extcomin_pwm) {
+ pwm_disable(par->extcomin_pwm);
+ pwm_put(par->extcomin_pwm);
+ }
+
+ fb_deferred_io_cleanup(info);
+ __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
+
+ kfree(par->spi_buf);
+
+ mutex_destroy(&par->update_lock);
+
+ framebuffer_release(info);
+
+ return 0;
+}
+
+static const struct spi_device_id smemlcd_spi_id[] = {
+ {"ls027b7dh01", 0},
+ {"ls044q7dh01", 0},
+ {"ls013b7dh05", 0},
+ {"ls013b7dh03", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, smemlcd_spi_id);
+
+static struct spi_driver smemlcd_driver = {
+ .probe = smemlcd_probe,
+ .remove = smemlcd_remove,
+ .id_table = smemlcd_spi_id,
+ .driver = {
+ .name = "smemlcdfb",
+ .of_match_table = smemlcd_of_match,
+ },
+};
+module_spi_driver(smemlcd_driver);
+
+MODULE_AUTHOR("Rodrigo Alencar <455.rodrigo.alencar@gmail.com>");
+MODULE_DESCRIPTION("Sharp Memory LCD Linux Framebuffer Driver");
+MODULE_LICENSE("GPL");
--
2.23.0.rc1
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2020-07-27 9:22 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-05 13:58 [PATCH] video: fbdev: added driver for sharp memory lcd displays Rodrigo Rolim Mendes de Alencar
2019-12-08 16:55 ` kbuild test robot
2019-12-08 20:25 ` Rodrigo Alencar
2020-07-24 20:34 Rodrigo Alencar
2020-07-26 17:22 ` Sam Ravnborg
2020-07-27 9:22 ` Daniel Vetter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).