Add abstraction for MIPI DCS/DBI like LCD controllers. Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx> --- drivers/staging/fbtft/Kconfig | 1 + drivers/staging/fbtft/Makefile | 1 + drivers/staging/fbtft/lcdctrl/Kconfig | 3 + drivers/staging/fbtft/lcdctrl/Makefile | 1 + drivers/staging/fbtft/lcdctrl/lcdctrl.c | 207 ++++++++++++++++++++++++++++++++ drivers/staging/fbtft/lcdctrl/lcdctrl.h | 104 ++++++++++++++++ 6 files changed, 317 insertions(+) create mode 100644 drivers/staging/fbtft/lcdctrl/Kconfig create mode 100644 drivers/staging/fbtft/lcdctrl/Makefile create mode 100644 drivers/staging/fbtft/lcdctrl/lcdctrl.c create mode 100644 drivers/staging/fbtft/lcdctrl/lcdctrl.h diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig index 2d1490f..0ac1b41 100644 --- a/drivers/staging/fbtft/Kconfig +++ b/drivers/staging/fbtft/Kconfig @@ -168,4 +168,5 @@ config FB_TFT_FBTFT_DEVICE tristate "Module to for adding FBTFT devices" depends on FB_TFT +source "drivers/staging/fbtft/lcdctrl/Kconfig" source "drivers/staging/fbtft/lcdreg/Kconfig" diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile index 2769421..69f7c2c 100644 --- a/drivers/staging/fbtft/Makefile +++ b/drivers/staging/fbtft/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_FB_TFT) += fbtft.o fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o obj-$(CONFIG_LCDREG) += lcdreg/ +obj-$(CONFIG_LCDCTRL) += lcdctrl/ # Controller drivers obj-$(CONFIG_FB_TFT_AGM1264K_FL) += fb_agm1264k-fl.o diff --git a/drivers/staging/fbtft/lcdctrl/Kconfig b/drivers/staging/fbtft/lcdctrl/Kconfig new file mode 100644 index 0000000..5272847 --- /dev/null +++ b/drivers/staging/fbtft/lcdctrl/Kconfig @@ -0,0 +1,3 @@ +config LCDCTRL + tristate + default n diff --git a/drivers/staging/fbtft/lcdctrl/Makefile b/drivers/staging/fbtft/lcdctrl/Makefile new file mode 100644 index 0000000..e6e4e8c --- /dev/null +++ b/drivers/staging/fbtft/lcdctrl/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_LCDCTRL) += lcdctrl.o diff --git a/drivers/staging/fbtft/lcdctrl/lcdctrl.c b/drivers/staging/fbtft/lcdctrl/lcdctrl.c new file mode 100644 index 0000000..028fc6b --- /dev/null +++ b/drivers/staging/fbtft/lcdctrl/lcdctrl.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2015 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include "lcdctrl.h" + +void lcdctrl_of_get_format(struct lcdctrl *ctrl) +{ + struct device *dev = ctrl->lcdreg->dev; + const char *fmt_str; + int ret; + + if (!ctrl->set_format) + return; + + ret = of_property_read_string(dev->of_node, "format", &fmt_str); + if (ret) + return; + + if (!strcmp(fmt_str, "monochrome")) + ctrl->format = LCDCTRL_FORMAT_MONO10; + else if (!strcmp(fmt_str, "rgb565")) + ctrl->format = LCDCTRL_FORMAT_RGB565; + else if (!strcmp(fmt_str, "rgb888")) + ctrl->format = LCDCTRL_FORMAT_RGB888; + else if (!strcmp(fmt_str, "xrgb8888")) + ctrl->format = LCDCTRL_FORMAT_XRGB8888; + else + dev_err(dev, "Invalid format: %s. Using default.\n", fmt_str); +} +EXPORT_SYMBOL(lcdctrl_of_get_format); + +void lcdctrl_of_get_rotation(struct lcdctrl *ctrl) +{ + struct device *dev = ctrl->lcdreg->dev; + u32 val; + int ret; + + if (!ctrl->rotate) + return; + + ret = of_property_read_u32(dev->of_node, "rotation", &val); + if (ret) { + if (ret != -EINVAL) + dev_err(dev, "error reading property 'rotation': %i\n", + ret); + return; + } + + switch (val) { + case 0: + case 90: + case 180: + case 270: + ctrl->rotation = val; + break; + default: + dev_err(dev, "illegal rotation value: %u\n", val); + break; + } +} +EXPORT_SYMBOL(lcdctrl_of_get_rotation); + +int lcdctrl_enable(struct lcdctrl *ctrl, void *videomem) +{ + struct lcdctrl_update update = { + .base = videomem, + .ys = 0, + .ye = lcdctrl_yres(ctrl) - 1, + }; + int ret; + + if (WARN_ON_ONCE(!ctrl->update)) + return -EINVAL; + + if (ctrl->initialized) + return 0; + + lcdreg_lock(ctrl->lcdreg); + + if (ctrl->power_supply) { + ret = regulator_enable(ctrl->power_supply); + if (ret) { + dev_err(ctrl->lcdreg->dev, + "failed to enable power supply: %d\n", ret); + goto enable_failed; + } + } + if (ctrl->poweron) { + ret = ctrl->poweron(ctrl); + if (ret) + goto enable_failed; + } + + if (ctrl->set_format) { + ret = ctrl->set_format(ctrl); + if (ret) { + dev_err(ctrl->lcdreg->dev, + "failed to set format '%d'\n", ctrl->format); + goto enable_failed; + } + } + + if (ctrl->rotate) { + ret = _lcdctrl_rotate(ctrl, ctrl->rotation); + if (ret) + goto enable_failed; + } + + ret = ctrl->update(ctrl, &update); + if (ret) + goto enable_failed; + + if (ctrl->blank) { + ret = ctrl->blank(ctrl, false); + if (ret) + goto enable_failed; + } + + ctrl->initialized = true; + +enable_failed: + lcdreg_unlock(ctrl->lcdreg); + + return ret; +} +EXPORT_SYMBOL(lcdctrl_enable); + +void lcdctrl_disable(struct lcdctrl *ctrl) +{ + lcdreg_lock(ctrl->lcdreg); + + if (ctrl->blank) + ctrl->blank(ctrl, true); + if (ctrl->poweroff) + ctrl->poweroff(ctrl); + if (ctrl->power_supply) + regulator_disable(ctrl->power_supply); + ctrl->initialized = false; + + lcdreg_unlock(ctrl->lcdreg); +} +EXPORT_SYMBOL(lcdctrl_disable); + +int lcdctrl_update(struct lcdctrl *ctrl, struct lcdctrl_update *update) +{ + u32 yres = lcdctrl_yres(ctrl); + int ret; + + if (!ctrl->initialized) + return -EINVAL; + + lcdreg_lock(ctrl->lcdreg); + + if (update->ys > update->ye) { + update->ys = 0; + update->ye = yres - 1; + } + if (update->ye >= yres) + update->ye = yres - 1; + + ret = ctrl->update(ctrl, update); + + lcdreg_unlock(ctrl->lcdreg); + + return ret; +} +EXPORT_SYMBOL(lcdctrl_update); + +/** + * Caller is responsible for locking. + */ +int _lcdctrl_rotate(struct lcdctrl *ctrl, u32 rotation) +{ + int ret; + + if (!ctrl->rotate) + return -ENOSYS; + + switch (rotation) { + case 0: + case 90: + case 180: + case 270: + break; + default: + return -EINVAL; + } + + ret = ctrl->rotate(ctrl, rotation); + if (!ret) + ctrl->rotation = rotation; + + return ret; +} +EXPORT_SYMBOL(_lcdctrl_rotate); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/fbtft/lcdctrl/lcdctrl.h b/drivers/staging/fbtft/lcdctrl/lcdctrl.h new file mode 100644 index 0000000..e74a66d --- /dev/null +++ b/drivers/staging/fbtft/lcdctrl/lcdctrl.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2015 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_LCDCTRL_H +#define __LINUX_LCDCTRL_H + +#include "../lcdreg/lcdreg.h" + +enum lcdctrl_format { + LCDCTRL_FORMAT_NONE = 0, + LCDCTRL_FORMAT_MONO10, + LCDCTRL_FORMAT_RGB565, + LCDCTRL_FORMAT_RGB888, + LCDCTRL_FORMAT_XRGB8888, +}; + +/** + * Description of a display update + * + * @base: Base address of video memory + * @ys: Horizontal line to start the transfer from (zero based) + * @ye: Last line to transfer (inclusive) + */ +struct lcdctrl_update { + void *base; + u32 ys; + u32 ye; +}; + +/** + * Description of LCD controller + * + * @width: Width of display in pixels + * @height: Height of display in pixels + * @rotation: Display rotation Counter Clockwise (0,90,180,270) + * @format: Videomemory format + * @initialized: Controller is initialized + * @poweron: Power on operation (optional) + * @poweroff: Power off operation (optional) + * @update: Updates the controllers video memory + * @rotate: Rotates the display (optional) + * @set_format: Sets format according to @format (optional) + * @blank: Blanks display (optional) + * @lcdreg: LCD register operated upon + * @driver_private: Driver data (not touched by core) + * @power_supply: Regulator for power supply + */ +struct lcdctrl { + u32 width; + u32 height; + u32 rotation; + enum lcdctrl_format format; + bool initialized; + + int (*poweron)(struct lcdctrl *ctrl); + void (*poweroff)(struct lcdctrl *ctrl); + int (*update)(struct lcdctrl *ctrl, struct lcdctrl_update *update); + int (*rotate)(struct lcdctrl *ctrl, u32 rotation); + int (*set_format)(struct lcdctrl *ctrl); + int (*blank)(struct lcdctrl *ctrl, bool blank); + + struct lcdreg *lcdreg; + struct regulator *power_supply; + void *driver_private; +}; + +static inline u32 lcdctrl_xres(struct lcdctrl *ctrl) +{ + return (ctrl->rotation % 180) ? ctrl->height : ctrl->width; +} + +static inline u32 lcdctrl_yres(struct lcdctrl *ctrl) +{ + return (ctrl->rotation % 180) ? ctrl->width : ctrl->height; +} + +static inline int lcdctrl_blank(struct lcdctrl *ctrl, bool blank) +{ + int ret; + + if (!ctrl->blank) + return -ENOSYS; + + lcdreg_lock(ctrl->lcdreg); + ret = ctrl->blank(ctrl, blank); + lcdreg_unlock(ctrl->lcdreg); + + return ret; +} + +extern void lcdctrl_of_get_format(struct lcdctrl *ctrl); +extern void lcdctrl_of_get_rotation(struct lcdctrl *ctrl); +extern int lcdctrl_enable(struct lcdctrl *ctrl, void *videomem); +extern void lcdctrl_disable(struct lcdctrl *ctrl); +extern int lcdctrl_update(struct lcdctrl *ctrl, struct lcdctrl_update *update); +extern int _lcdctrl_rotate(struct lcdctrl *ctrl, u32 rotate); + +#endif /* __LINUX_LCDCTRL_H */ -- 2.2.2 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel