[RFC 6/7] staging: fbtft: extend core to use lcdctrl
Noralf Trønnes
noralf at tronnes.org
Mon Mar 2 10:54:28 UTC 2015
Add lcdctrl support in core fbtft module.
Provide new API for drivers to use.
Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
---
drivers/staging/fbtft/Makefile | 1 +
drivers/staging/fbtft/fbtft-lcdctrl.c | 311 ++++++++++++++++++++++++++++++++++
drivers/staging/fbtft/fbtft.h | 13 ++
3 files changed, 325 insertions(+)
create mode 100644 drivers/staging/fbtft/fbtft-lcdctrl.c
diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile
index 69f7c2c..6f002a6 100644
--- a/drivers/staging/fbtft/Makefile
+++ b/drivers/staging/fbtft/Makefile
@@ -1,6 +1,7 @@
# Core modules
obj-$(CONFIG_FB_TFT) += fbtft.o
fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o
+fbtft-$(if $(CONFIG_LCDCTRL),y,) += fbtft-lcdctrl.o
obj-$(CONFIG_LCDREG) += lcdreg/
obj-$(CONFIG_LCDCTRL) += lcdctrl/
diff --git a/drivers/staging/fbtft/fbtft-lcdctrl.c b/drivers/staging/fbtft/fbtft-lcdctrl.c
new file mode 100644
index 0000000..b4e6011
--- /dev/null
+++ b/drivers/staging/fbtft/fbtft-lcdctrl.c
@@ -0,0 +1,311 @@
+#include <linux/device.h>
+#include <linux/regulator/consumer.h>
+
+#include "fbtft.h"
+
+extern void fbtft_sysfs_init(struct fbtft_par *par);
+
+static int fbtft_lcdctrl_blank(struct fbtft_par *par, bool on)
+{
+ struct lcdctrl *ctrl = par->lcdctrl;
+
+ if (ctrl->blank)
+ return lcdctrl_blank(ctrl, on);
+ else if (par->info->bl_dev)
+ return 0; /* backlight subsystem handles blanking */
+ else
+ return -EINVAL; /* no blanking support */
+}
+
+static void fbtft_lcdctrl_update_display(struct fbtft_par *par,
+ unsigned start_line, unsigned end_line)
+{
+ struct lcdctrl *ctrl = par->lcdctrl;
+ struct lcdctrl_update update = {
+ .base = (void __force *)par->info->screen_base,
+ .ys = start_line,
+ .ye = end_line,
+ };
+ int ret;
+
+ ret = lcdctrl_update(ctrl, &update);
+ if (ret)
+ pr_err_once("%s %s: lcdctrl_update failed: %i\n",
+ dev_driver_string(ctrl->lcdreg->dev),
+ dev_name(ctrl->lcdreg->dev), ret);
+}
+
+static int fbtft_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ u32 rotate = var->rotate;
+
+ *var = info->var;
+ var->rotate = rotate;
+
+ switch (var->rotate) {
+ case 0:
+ case 90:
+ case 180:
+ case 270:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int fbtft_fb_set_par(struct fb_info *info)
+{
+ struct fbtft_par *par = info->par;
+ struct lcdctrl *ctrl = par->lcdctrl;
+ int ret;
+
+ lcdreg_lock(ctrl->lcdreg);
+
+ ret = _lcdctrl_rotate(ctrl, info->var.rotate);
+ if (ret) {
+ info->var.rotate = ctrl->rotation;
+ goto rotate_failed;
+ }
+
+ info->var.xres = lcdctrl_xres(ctrl);
+ info->var.yres = lcdctrl_yres(ctrl);
+ info->var.xres_virtual = info->var.xres;
+ info->var.yres_virtual = info->var.yres;
+
+ switch (info->var.bits_per_pixel) {
+ case 1:
+ info->fix.line_length = info->var.xres / 8;
+ break;
+ case 8:
+ info->fix.line_length = info->var.xres;
+ break;
+ case 16:
+ info->fix.line_length = info->var.xres * 2;
+ break;
+ case 24:
+ info->fix.line_length = info->var.xres * 3;
+ break;
+ case 32:
+ info->fix.line_length = info->var.xres * 4;
+ break;
+ }
+
+rotate_failed:
+ lcdreg_unlock(ctrl->lcdreg);
+
+ return ret;
+}
+
+static struct fb_info *fbtft_lcdctrl_register(struct lcdctrl *ctrl)
+{
+ struct device *dev = ctrl->lcdreg->dev;
+ struct fb_info *info;
+ struct fbtft_par *par;
+ int ret;
+ struct fbtft_display display = {
+ .width = lcdctrl_xres(ctrl),
+ .height = lcdctrl_yres(ctrl),
+ .fps = 20,
+ .txbuflen = -2, /* don't allocate txbuf */
+ };
+
+ switch (ctrl->format) {
+ case LCDCTRL_FORMAT_MONO10:
+ display.bpp = 1;
+ break;
+ case LCDCTRL_FORMAT_RGB565:
+ display.bpp = 16;
+ break;
+ case LCDCTRL_FORMAT_RGB888:
+ display.bpp = 24;
+ break;
+ case LCDCTRL_FORMAT_XRGB8888:
+ display.bpp = 32;
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+
+ info = fbtft_framebuffer_alloc(&display, dev);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ switch (ctrl->format) {
+ case LCDCTRL_FORMAT_MONO10:
+ info->fix.visual = FB_VISUAL_MONO10;
+ info->var.red.length = 1;
+ info->var.red.offset = 0;
+ info->var.green.length = 1;
+ info->var.green.offset = 0;
+ info->var.blue.length = 1;
+ info->var.blue.offset = 0;
+ break;
+ case LCDCTRL_FORMAT_RGB565:
+ /* set by fbtft_framebuffer_alloc() */
+ break;
+ case LCDCTRL_FORMAT_RGB888:
+ info->var.red.offset = 16;
+ info->var.red.length = 8;
+ info->var.green.offset = 8;
+ info->var.green.length = 8;
+ info->var.blue.offset = 0;
+ info->var.blue.length = 8;
+ break;
+ case LCDCTRL_FORMAT_XRGB8888:
+ info->var.red.offset = 16;
+ info->var.red.length = 8;
+ info->var.green.offset = 8;
+ info->var.green.length = 8;
+ info->var.blue.offset = 0;
+ info->var.blue.length = 8;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_release;
+ }
+
+ par = info->par;
+ par->lcdctrl = ctrl;
+ par->fbtftops.blank = fbtft_lcdctrl_blank;
+ par->fbtftops.update_display = fbtft_lcdctrl_update_display;
+ if (ctrl->rotate) {
+ info->var.rotate = ctrl->rotation;
+ info->fbops->fb_check_var = fbtft_fb_check_var;
+ info->fbops->fb_set_par = fbtft_fb_set_par;
+ }
+
+ ret = lcdctrl_enable(ctrl, info->screen_base);
+ if (ret)
+ goto err_release;
+
+ ret = register_framebuffer(info);
+ if (ret)
+ goto err_release;
+
+ fbtft_sysfs_init(par);
+ dev_set_drvdata(dev, info);
+
+ dev_info(info->dev, "%s frame buffer, %ux%u, %u KiB video memory\n",
+ info->fix.id, info->var.xres, info->var.yres,
+ info->fix.smem_len >> 10);
+
+ return info;
+
+err_release:
+ fbtft_framebuffer_release(info);
+
+ return ERR_PTR(ret);
+}
+
+static void fbtft_lcdctrl_release(struct fb_info *info)
+{
+ struct fbtft_par *par = info->par;
+ struct lcdctrl *ctrl = par->lcdctrl;
+
+ fb_blank(info, FB_BLANK_POWERDOWN);
+ if (info->bl_dev) {
+ put_device(&info->bl_dev->dev);
+ info->bl_dev = NULL;
+ }
+ lcdctrl_disable(ctrl);
+ fbtft_unregister_framebuffer(info);
+ fbtft_framebuffer_release(info);
+}
+
+static void devm_lcdctrl_release(struct device *dev, void *res)
+{
+ fbtft_lcdctrl_release(*(struct fb_info **)res);
+}
+
+int devm_fbtft_lcdctrl_register(struct lcdctrl *ctrl)
+{
+ struct fb_info **ptr;
+
+ if (WARN_ON(!ctrl || !ctrl->lcdreg || !ctrl->lcdreg->dev))
+ return -EINVAL;
+
+ ptr = devres_alloc(devm_lcdctrl_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ *ptr = fbtft_lcdctrl_register(ctrl);
+ if (IS_ERR(*ptr)) {
+ int ret = PTR_ERR(*ptr);
+
+ devres_free(ptr);
+ return ret;
+ }
+
+ devres_add(ctrl->lcdreg->dev, ptr);
+
+ return 0;
+}
+EXPORT_SYMBOL(devm_fbtft_lcdctrl_register);
+
+int devm_fbtft_lcdctrl_of_register(struct lcdctrl *ctrl)
+{
+ struct device *dev;
+ struct device_node *backlight_node;
+ struct backlight_device *backlight = NULL;
+ int ret;
+
+ if (WARN_ON(!ctrl || !ctrl->lcdreg || !ctrl->lcdreg->dev))
+ return -EINVAL;
+
+ dev = ctrl->lcdreg->dev;
+ ctrl->initialized = of_property_read_bool(dev->of_node, "initialized");
+ ctrl->power_supply = devm_regulator_get(dev, "power");
+ if (IS_ERR(ctrl->power_supply))
+ return PTR_ERR(ctrl->power_supply);
+
+ backlight_node = of_parse_phandle(dev->of_node, "backlight", 0);
+ if (backlight_node) {
+ backlight = of_find_backlight_by_node(backlight_node);
+ of_node_put(backlight_node);
+ if (!backlight)
+ return -EPROBE_DEFER;
+ }
+
+ lcdctrl_of_get_format(ctrl);
+ lcdctrl_of_get_rotation(ctrl);
+
+ ret = devm_fbtft_lcdctrl_register(ctrl);
+ if (ret)
+ return ret;
+
+ if (backlight) {
+ struct fb_info *info = dev_get_drvdata(dev);
+
+ info->bl_dev = backlight;
+ get_device(&info->bl_dev->dev);
+ if (backlight->props.brightness == 0)
+ backlight->props.brightness = backlight->props.max_brightness;
+ fb_blank(info, FB_BLANK_UNBLANK);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(devm_fbtft_lcdctrl_of_register);
+
+#ifdef CONFIG_PM_SLEEP
+static int fbtft_pm_suspend(struct device *dev)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct fbtft_par *par = info->par;
+
+ lcdctrl_disable(par->lcdctrl);
+
+ return 0;
+}
+
+static int fbtft_pm_resume(struct device *dev)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct fbtft_par *par = info->par;
+
+ return lcdctrl_enable(par->lcdctrl, info->screen_base);
+}
+
+SIMPLE_DEV_PM_OPS(fbtft_pm_ops, fbtft_pm_suspend, fbtft_pm_resume);
+EXPORT_SYMBOL(fbtft_pm_ops);
+#endif
diff --git a/drivers/staging/fbtft/fbtft.h b/drivers/staging/fbtft/fbtft.h
index 0dbf3f9..d64aded 100644
--- a/drivers/staging/fbtft/fbtft.h
+++ b/drivers/staging/fbtft/fbtft.h
@@ -24,6 +24,8 @@
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
+#include "lcdctrl/lcdctrl.h"
+
#define FBTFT_NOP 0x00
#define FBTFT_SWRESET 0x01
@@ -214,6 +216,7 @@ struct fbtft_platform_data {
* @extra: Extra info needed by driver
*/
struct fbtft_par {
+ struct lcdctrl *lcdctrl;
struct spi_device *spi;
struct platform_device *pdev;
struct fb_info *info;
@@ -298,6 +301,16 @@ extern void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...);
extern void fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...);
extern void fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...);
+/* fbtft-lcdctrl.c */
+extern int devm_fbtft_lcdctrl_register(struct lcdctrl *ctrl);
+extern int devm_fbtft_lcdctrl_of_register(struct lcdctrl *ctrl);
+
+#ifdef CONFIG_PM_SLEEP
+extern const struct dev_pm_ops fbtft_pm_ops;
+#define FBTFT_PM_OPS (&fbtft_pm_ops)
+#else
+#define FBTFT_PM_OPS NULL
+#endif
#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display) \
\
--
2.2.2
More information about the devel
mailing list