Add lcdctrl support in core fbtft module. Provide new API for drivers to use. Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx> --- 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 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel