diff options
-rw-r--r-- | drivers/video/Kconfig | 1 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/display/Kconfig | 10 | ||||
-rw-r--r-- | drivers/video/display/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/display/display-core.c | 295 | ||||
-rw-r--r-- | include/video/display.h | 230 |
6 files changed, 538 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 2e937bdace6..6d9788d0932 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2475,6 +2475,7 @@ source "drivers/video/omap2/Kconfig" source "drivers/video/exynos/Kconfig" source "drivers/video/mmp/Kconfig" source "drivers/video/backlight/Kconfig" +source "drivers/video/display/Kconfig" if VT source "drivers/video/console/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index e8bae8dd480..d7fd4a217a0 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -15,6 +15,7 @@ fb-objs := $(fb-y) obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ +obj-y += display/ obj-$(CONFIG_EXYNOS_VIDEO) += exynos/ diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig new file mode 100644 index 00000000000..477192d69af --- /dev/null +++ b/drivers/video/display/Kconfig @@ -0,0 +1,10 @@ +menuconfig DISPLAY_CORE + tristate "Display Core" + ---help--- + Support common display framework for graphics devices. + +if DISPLAY_CORE + + + +endif # DISPLAY_CORE diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile new file mode 100644 index 00000000000..bd93496d2cf --- /dev/null +++ b/drivers/video/display/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_DISPLAY_CORE) += display-core.o diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c new file mode 100644 index 00000000000..9c93aae3926 --- /dev/null +++ b/drivers/video/display/display-core.c @@ -0,0 +1,295 @@ +/* + * Display Core + * + * Copyright (C) 2012 Renesas Solutions Corp. + * + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> + +#include <video/display.h> +#include <video/videomode.h> + +static struct video_source *video_source_bind(struct display_entity *entity); +static void video_source_unbind(struct display_entity *entity); + +/* ----------------------------------------------------------------------------- + * Display Entity + */ + +static LIST_HEAD(display_entity_list); +static DEFINE_MUTEX(display_entity_mutex); + +struct display_entity *display_entity_get_first(void) +{ + /* FIXME: Don't we need some locking here? */ + + if (list_empty(&display_entity_list)) + return NULL; + + return list_first_entry(&display_entity_list, struct display_entity, + list); +} +EXPORT_SYMBOL(display_entity_get_first); + +int display_entity_set_state(struct display_entity *entity, + enum display_entity_state state) +{ + int ret; + + if (entity->state == state) + return 0; + + if (!entity->ops || !entity->ops->set_state) + return 0; + + ret = entity->ops->set_state(entity, state); + if (ret < 0) + return ret; + + entity->state = state; + return 0; +} +EXPORT_SYMBOL_GPL(display_entity_set_state); + +int display_entity_get_modes(struct display_entity *entity, + const struct videomode **modes) +{ + if (!entity->ops || !entity->ops->get_modes) + return 0; + + return entity->ops->get_modes(entity, modes); +} +EXPORT_SYMBOL_GPL(display_entity_get_modes); + +int display_entity_get_size(struct display_entity *entity, + unsigned int *width, unsigned int *height) +{ + if (!entity->ops || !entity->ops->get_size) + return -EOPNOTSUPP; + + return entity->ops->get_size(entity, width, height); +} +EXPORT_SYMBOL_GPL(display_entity_get_size); + +int display_entity_get_params(struct display_entity *entity, + struct display_entity_interface_params *params) +{ + if (!entity->ops || !entity->ops->get_params) + return -EOPNOTSUPP; + + return entity->ops->get_params(entity, params); +} +EXPORT_SYMBOL_GPL(display_entity_get_params); + +static void display_entity_release(struct kref *ref) +{ + struct display_entity *entity = + container_of(ref, struct display_entity, ref); + + if (entity->release) + entity->release(entity); +} + +struct display_entity *display_entity_get(struct display_entity *entity) +{ + if (entity == NULL) + return NULL; + + kref_get(&entity->ref); + return entity; +} +EXPORT_SYMBOL_GPL(display_entity_get); + +void display_entity_put(struct display_entity *entity) +{ + kref_put(&entity->ref, display_entity_release); +} +EXPORT_SYMBOL_GPL(display_entity_put); + +int __must_check __display_entity_register(struct display_entity *entity, + struct module *owner) +{ + struct video_source *src; + + kref_init(&entity->ref); + entity->owner = owner; + entity->state = DISPLAY_ENTITY_STATE_OFF; + entity->source = NULL; + + src = video_source_bind(entity); + if (!src) + return -EPROBE_DEFER; + + mutex_lock(&display_entity_mutex); + list_add(&entity->list, &display_entity_list); + mutex_unlock(&display_entity_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(__display_entity_register); + +void display_entity_unregister(struct display_entity *entity) +{ + video_source_unbind(entity); + + mutex_lock(&display_entity_mutex); + + list_del(&entity->list); + mutex_unlock(&display_entity_mutex); + + display_entity_put(entity); +} +EXPORT_SYMBOL_GPL(display_entity_unregister); + +/* ----------------------------------------------------------------------------- + * Video Source + */ + +static LIST_HEAD(video_source_list); +static DEFINE_MUTEX(video_source_mutex); + +static void video_source_release(struct kref *ref) +{ + struct video_source *src = + container_of(ref, struct video_source, ref); + + if (src->release) + src->release(src); +} + +static struct video_source *video_source_get(struct video_source *src) +{ + if (src == NULL) + return NULL; + + kref_get(&src->ref); + if (!try_module_get(src->owner)) { + kref_put(&src->ref, video_source_release); + return NULL; + } + + return src; +} + +static void video_source_put(struct video_source *src) +{ + module_put(src->owner); + kref_put(&src->ref, video_source_release); +} + +int __must_check __video_source_register(struct video_source *src, + struct module *owner) +{ + kref_init(&src->ref); + src->owner = owner; + + mutex_lock(&video_source_mutex); + list_add(&src->list, &video_source_list); + + mutex_unlock(&video_source_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(__video_source_register); + +void video_source_unregister(struct video_source *src) +{ + mutex_lock(&video_source_mutex); + + list_del(&src->list); + mutex_unlock(&video_source_mutex); + + kref_put(&src->ref, video_source_release); +} +EXPORT_SYMBOL_GPL(video_source_unregister); + +static struct video_source *video_source_bind(struct display_entity *entity) +{ + struct video_source *src = NULL; + int ret; + + if (entity->source) + return entity->source; + + mutex_lock(&video_source_mutex); + + if (entity->of_node) { + struct device_node *np; + + np = of_parse_phandle(entity->of_node, "video-source", 0); + if (!np) + goto unlock; + + list_for_each_entry(src, &video_source_list, list) { + if (src->of_node == np) + goto found; + } + + src = NULL; + goto unlock; + } + + if (!entity->src_name) + goto unlock; + + list_for_each_entry(src, &video_source_list, list) { + if (src->id != entity->src_id) + continue; + if (!strcmp(src->name, entity->src_name)) + goto found; + } + + src = NULL; + goto unlock; + +found: + video_source_get(src); + + if (src->common_ops->bind) { + ret = src->common_ops->bind(src, entity); + if (ret != 0) { + video_source_put(src); + src = NULL; + goto unlock; + } + } + + src->sink = entity; + entity->source = src; + +unlock: + mutex_unlock(&video_source_mutex); + + return src; +} + +static void video_source_unbind(struct display_entity *entity) +{ + struct video_source *src = entity->source; + + if (!src) + return; + + if (src->common_ops && src->common_ops->unbind) + src->common_ops->unbind(src, entity); + + src->sink = NULL; + entity->source = NULL; + + video_source_put(src); +} + +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_DESCRIPTION("Display Core"); +MODULE_LICENSE("GPL"); diff --git a/include/video/display.h b/include/video/display.h new file mode 100644 index 00000000000..11d7032b7a6 --- /dev/null +++ b/include/video/display.h @@ -0,0 +1,230 @@ +/* + * Display Core + * + * Copyright (C) 2012 Renesas Solutions Corp. + * + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DISPLAY_H__ +#define __DISPLAY_H__ + +#include <linux/kref.h> +#include <linux/list.h> +#include <linux/module.h> +#include <video/omapdss.h> + +struct display_entity; +struct video_source; +struct videomode; + +/* ----------------------------------------------------------------------------- + * Display Entity + */ + +/* Hack to get the first registered display entity */ +struct display_entity *display_entity_get_first(void); + +enum display_entity_state { + DISPLAY_ENTITY_STATE_OFF, + DISPLAY_ENTITY_STATE_STANDBY, + DISPLAY_ENTITY_STATE_ON, +}; + +enum display_entity_interface_type { + DISPLAY_ENTITY_INTERFACE_DPI, + DISPLAY_ENTITY_INTERFACE_DSI, +}; + +#define DSI_MODE_VIDEO (1 << 0) +#define DSI_MODE_VIDEO_BURST (1 << 1) +#define DSI_MODE_VIDEO_SYNC_PULSE (1 << 2) +#define DSI_MODE_VIDEO_AUTO_VERT (1 << 3) +#define DSI_MODE_VIDEO_HSE (1 << 4) +#define DSI_MODE_VIDEO_HFP (1 << 5) +#define DSI_MODE_VIDEO_HBP (1 << 6) +#define DSI_MODE_VIDEO_HSA (1 << 7) +#define DSI_MODE_VSYNC_FLUSH (1 << 8) +#define DSI_MODE_EOT_PACKET (1 << 9) + +enum mipi_dsi_pixel_format { + DSI_FMT_RGB888, + DSI_FMT_RGB666, + DSI_FMT_RGB666_PACKED, + DSI_FMT_RGB565, +}; + +struct mipi_dsi_interface_params { + enum mipi_dsi_pixel_format format; + unsigned long mode; + unsigned long hs_clk_freq; + unsigned long esc_clk_freq; + unsigned char data_lanes; + unsigned char cmd_allow; +}; + +struct display_entity_interface_params { + enum display_entity_interface_type type; + union { + struct mipi_dsi_interface_params dsi; + } p; +}; + +struct display_entity_control_ops { + int (*set_state)(struct display_entity *ent, + enum display_entity_state state); + int (*update)(struct display_entity *ent, + void (*callback)(int, void *), void *data); + int (*get_modes)(struct display_entity *ent, + const struct videomode **modes); + int (*get_params)(struct display_entity *ent, + struct display_entity_interface_params *params); + int (*get_size)(struct display_entity *ent, + unsigned int *width, unsigned int *height); +}; + +struct display_entity { + struct list_head list; + struct device *dev; + struct device_node *of_node; + struct module *owner; + struct kref ref; + + const char *src_name; + int src_id; + struct video_source *source; + + const struct display_entity_control_ops *ops; + + void(*release)(struct display_entity *ent); + + enum display_entity_state state; +}; + +int display_entity_set_state(struct display_entity *entity, + enum display_entity_state state); +int display_entity_get_params(struct display_entity *entity, + struct display_entity_interface_params *params); +int display_entity_get_modes(struct display_entity *entity, + const struct videomode **modes); +int display_entity_get_size(struct display_entity *entity, + unsigned int *width, unsigned int *height); + +struct display_entity *display_entity_get(struct display_entity *entity); +void display_entity_put(struct display_entity *entity); + +int __must_check __display_entity_register(struct display_entity *entity, + struct module *owner); +void display_entity_unregister(struct display_entity *entity); + +#define display_entity_register(display_entity) \ + __display_entity_register(display_entity, THIS_MODULE) + + +/* ----------------------------------------------------------------------------- + * Video Source + */ + +enum video_source_stream_state { + DISPLAY_ENTITY_STREAM_STOPPED, + DISPLAY_ENTITY_STREAM_CONTINUOUS, +}; + +struct common_video_source_ops { + int (*set_stream)(struct video_source *src, + enum video_source_stream_state state); + int (*bind)(struct video_source *src, struct display_entity *sink); + int (*unbind)(struct video_source *src, struct display_entity *sink); +}; + +struct dpi_video_source_ops { + int (*set_videomode)(struct video_source *src, + const struct videomode *vm); + int (*set_data_lines)(struct video_source *src, int lines); +}; + +struct dsi_video_source_ops { + /* enable/disable dsi bus */ + int (*enable)(struct video_source *src); + int (*disable)(struct video_source *src); + + /* bus configuration */ + int (*configure_pins)(struct video_source *src, + const struct omap_dsi_pin_config *pins); + int (*set_clocks)(struct video_source *src, + unsigned long ddr_clk, + unsigned long lp_clk); + /* NOTE: Do we really need configure_pins and set_clocks here? */ + + void (*enable_hs)(struct video_source *src, bool enable); + + /* data transfer */ + int (*dcs_write)(struct video_source *src, int channel, + const u8 *data, size_t len); + int (*dcs_read)(struct video_source *src, int channel, u8 dcs_cmd, + u8 *data, size_t len); + /* NOTE: Do we need more write and read types? */ + + int (*update)(struct video_source *src, int channel, + void (*callback)(int, void *), void *data); +}; + +struct dvi_video_source_ops { + int (*set_videomode)(struct video_source *src, + const struct videomode *vm); +}; + +struct video_source { + struct list_head list; + struct device *dev; + struct device_node *of_node; + struct module *owner; + struct kref ref; + + struct display_entity *sink; + + const char *name; + int id; + + const struct common_video_source_ops *common_ops; + + union { + const struct dpi_video_source_ops *dpi; + const struct dsi_video_source_ops *dsi; + const struct dvi_video_source_ops *dvi; + } ops; + + void(*release)(struct video_source *src); +}; + +static inline int dsi_dcs_write(struct video_source *src, int channel, + const u8 *data, size_t len) +{ + if (!src->ops.dsi || !src->ops.dsi->dcs_write) + return -EINVAL; + + return src->ops.dsi->dcs_write(src, channel, data, len); +} + +static inline int dsi_dcs_read(struct video_source *src, int channel, + u8 dcs_cmd, u8 *data, size_t len) +{ + if (!src->ops.dsi || !src->ops.dsi->dcs_read) + return -EINVAL; + + return src->ops.dsi->dcs_read(src, channel, dcs_cmd, data, len); +} + + +#define video_source_register(video_source) \ + __video_source_register(video_source, THIS_MODULE) + +int __must_check __video_source_register(struct video_source *entity, + struct module *owner); +void video_source_unregister(struct video_source *entity); + +#endif /* __DISPLAY_H__ */ |