diff options
Diffstat (limited to 'drivers/video/mcde/mcde_dss.c')
-rw-r--r-- | drivers/video/mcde/mcde_dss.c | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/drivers/video/mcde/mcde_dss.c b/drivers/video/mcde/mcde_dss.c new file mode 100644 index 00000000000..35a6cbe9540 --- /dev/null +++ b/drivers/video/mcde/mcde_dss.c @@ -0,0 +1,445 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * ST-Ericsson MCDE display sub system driver + * + * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com> + * for ST-Ericsson. + * + * License terms: GNU General Public License (GPL), version 2. + */ + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/slab.h> + +#include <video/mcde_dss.h> + +#define to_overlay(x) container_of(x, struct mcde_overlay, kobj) + +void overlay_release(struct kobject *kobj) +{ + struct mcde_overlay *ovly = to_overlay(kobj); + + kfree(ovly); +} + +struct kobj_type ovly_type = { + .release = overlay_release, +}; + +static int apply_overlay(struct mcde_overlay *ovly, + struct mcde_overlay_info *info, bool force) +{ + int ret = 0; + if (ovly->ddev->invalidate_area) { + /* TODO: transform ovly coord to screen coords (vmode): + * add offset + */ + struct mcde_rectangle dirty = info->dirty; + mutex_lock(&ovly->ddev->display_lock); + ret = ovly->ddev->invalidate_area(ovly->ddev, &dirty); + mutex_unlock(&ovly->ddev->display_lock); + } + + if (ovly->info.paddr != info->paddr || force) + mcde_ovly_set_source_buf(ovly->state, info->paddr); + + if (ovly->info.stride != info->stride || ovly->info.fmt != info->fmt || + force) + mcde_ovly_set_source_info(ovly->state, info->stride, info->fmt); + if (ovly->info.src_x != info->src_x || + ovly->info.src_y != info->src_y || + ovly->info.w != info->w || + ovly->info.h != info->h || force) + mcde_ovly_set_source_area(ovly->state, + info->src_x, info->src_y, info->w, info->h); + if (ovly->info.dst_x != info->dst_x || ovly->info.dst_y != info->dst_y + || ovly->info.dst_z != info->dst_z || + force) + mcde_ovly_set_dest_pos(ovly->state, + info->dst_x, info->dst_y, info->dst_z); + + mcde_ovly_apply(ovly->state); + ovly->info = *info; + + return ret; +} + +/* MCDE DSS operations */ + +int mcde_dss_open_channel(struct mcde_display_device *ddev) +{ + int ret = 0; + struct mcde_chnl_state *chnl; + + mutex_lock(&ddev->display_lock); + /* Acquire MCDE resources */ + chnl = mcde_chnl_get(ddev->chnl_id, ddev->fifo, ddev->port); + if (IS_ERR(chnl)) { + ret = PTR_ERR(chnl); + dev_warn(&ddev->dev, "Failed to acquire MCDE channel\n"); + goto chnl_get_failed; + } + ddev->chnl_state = chnl; +chnl_get_failed: + mutex_unlock(&ddev->display_lock); + return ret; +} +EXPORT_SYMBOL(mcde_dss_open_channel); + +void mcde_dss_close_channel(struct mcde_display_device *ddev) +{ + mutex_lock(&ddev->display_lock); + mcde_chnl_put(ddev->chnl_state); + ddev->chnl_state = NULL; + mutex_unlock(&ddev->display_lock); +} +EXPORT_SYMBOL(mcde_dss_close_channel); + +int mcde_dss_enable_display(struct mcde_display_device *ddev) +{ + int ret; + + if (ddev->enabled) + return 0; + + mutex_lock(&ddev->display_lock); + mcde_chnl_enable(ddev->chnl_state); + + /* Initiate display communication */ + ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_STANDBY); + if (ret < 0) { + dev_warn(&ddev->dev, "Failed to initialize display\n"); + goto display_failed; + } + + ret = ddev->set_rotation(ddev, ddev->get_rotation(ddev)); + if (ret < 0) + dev_warn(&ddev->dev, "Failed to set rotation\n"); + + dev_dbg(&ddev->dev, "Display enabled, chnl=%d\n", + ddev->chnl_id); + ddev->enabled = true; + mutex_unlock(&ddev->display_lock); + + return 0; + +display_failed: + mcde_chnl_disable(ddev->chnl_state); + mutex_unlock(&ddev->display_lock); + return ret; +} +EXPORT_SYMBOL(mcde_dss_enable_display); + +void mcde_dss_disable_display(struct mcde_display_device *ddev) +{ + if (!ddev->enabled) + return; + + /* TODO: Disable overlays */ + mutex_lock(&ddev->display_lock); + + mcde_chnl_stop_flow(ddev->chnl_state); + + (void)ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF); + + mcde_chnl_disable(ddev->chnl_state); + + ddev->enabled = false; + mutex_unlock(&ddev->display_lock); + + dev_dbg(&ddev->dev, "Display disabled, chnl=%d\n", ddev->chnl_id); +} +EXPORT_SYMBOL(mcde_dss_disable_display); + +int mcde_dss_apply_channel(struct mcde_display_device *ddev) +{ + int ret; + if (!ddev->apply_config) + return -EINVAL; + mutex_lock(&ddev->display_lock); + ret = ddev->apply_config(ddev); + mutex_unlock(&ddev->display_lock); + + return ret; +} +EXPORT_SYMBOL(mcde_dss_apply_channel); + +struct mcde_overlay *mcde_dss_create_overlay(struct mcde_display_device *ddev, + struct mcde_overlay_info *info) +{ + struct mcde_overlay *ovly; + + ovly = kzalloc(sizeof(struct mcde_overlay), GFP_KERNEL); + if (!ovly) + return NULL; + + kobject_init(&ovly->kobj, &ovly_type); /* Local ref */ + kobject_get(&ovly->kobj); /* Creator ref */ + INIT_LIST_HEAD(&ovly->list); + mutex_lock(&ddev->display_lock); + list_add(&ddev->ovlys, &ovly->list); + mutex_unlock(&ddev->display_lock); + ovly->info = *info; + ovly->ddev = ddev; + + return ovly; +} +EXPORT_SYMBOL(mcde_dss_create_overlay); + +void mcde_dss_destroy_overlay(struct mcde_overlay *ovly) +{ + list_del(&ovly->list); + if (ovly->state) + mcde_dss_disable_overlay(ovly); + kobject_put(&ovly->kobj); +} +EXPORT_SYMBOL(mcde_dss_destroy_overlay); + +int mcde_dss_enable_overlay(struct mcde_overlay *ovly) +{ + int ret; + + if (!ovly->ddev->chnl_state) + return -EINVAL; + + if (!ovly->state) { + struct mcde_ovly_state *state; + state = mcde_ovly_get(ovly->ddev->chnl_state); + if (IS_ERR(state)) { + ret = PTR_ERR(state); + dev_warn(&ovly->ddev->dev, + "Failed to acquire overlay\n"); + return ret; + } + ovly->state = state; + } + + apply_overlay(ovly, &ovly->info, true); + + dev_vdbg(&ovly->ddev->dev, "Overlay enabled, chnl=%d\n", + ovly->ddev->chnl_id); + return 0; +} +EXPORT_SYMBOL(mcde_dss_enable_overlay); + +int mcde_dss_apply_overlay(struct mcde_overlay *ovly, + struct mcde_overlay_info *info) +{ + if (info == NULL) + info = &ovly->info; + return apply_overlay(ovly, info, false); +} +EXPORT_SYMBOL(mcde_dss_apply_overlay); + +void mcde_dss_disable_overlay(struct mcde_overlay *ovly) +{ + if (!ovly->state) + return; + + mcde_ovly_put(ovly->state); + + dev_dbg(&ovly->ddev->dev, "Overlay disabled, chnl=%d\n", + ovly->ddev->chnl_id); + + ovly->state = NULL; +} +EXPORT_SYMBOL(mcde_dss_disable_overlay); + +int mcde_dss_update_overlay(struct mcde_overlay *ovly, bool tripple_buffer) +{ + int ret; + dev_vdbg(&ovly->ddev->dev, "Overlay update, chnl=%d\n", + ovly->ddev->chnl_id); + + if (!ovly->state || !ovly->ddev->update || !ovly->ddev->invalidate_area) + return -EINVAL; + + mutex_lock(&ovly->ddev->display_lock); + /* Do not perform an update if power mode is off */ + if (ovly->ddev->get_power_mode(ovly->ddev) == MCDE_DISPLAY_PM_OFF) { + ret = 0; + goto power_mode_off; + } + + ret = ovly->ddev->update(ovly->ddev, tripple_buffer); + if (ret) + goto update_failed; + + ret = ovly->ddev->invalidate_area(ovly->ddev, NULL); + +power_mode_off: +update_failed: + mutex_unlock(&ovly->ddev->display_lock); + return ret; +} +EXPORT_SYMBOL(mcde_dss_update_overlay); + +void mcde_dss_get_overlay_info(struct mcde_overlay *ovly, + struct mcde_overlay_info *info) { + if (info) + *info = ovly->info; +} +EXPORT_SYMBOL(mcde_dss_get_overlay_info); + +void mcde_dss_get_native_resolution(struct mcde_display_device *ddev, + u16 *x_res, u16 *y_res) +{ + u16 x_tmp, y_tmp; + mutex_lock(&ddev->display_lock); + ddev->get_native_resolution(ddev, &x_tmp, &y_tmp); + if (ddev->orientation == MCDE_DISPLAY_ROT_90_CW || + ddev->orientation == MCDE_DISPLAY_ROT_90_CCW) { + *x_res = y_tmp; + *y_res = x_tmp; + } else { + *x_res = x_tmp; + *y_res = y_tmp; + } + mutex_unlock(&ddev->display_lock); +} +EXPORT_SYMBOL(mcde_dss_get_native_resolution); + +enum mcde_ovly_pix_fmt mcde_dss_get_default_pixel_format( + struct mcde_display_device *ddev) +{ + int ret; + mutex_lock(&ddev->display_lock); + ret = ddev->get_default_pixel_format(ddev); + mutex_unlock(&ddev->display_lock); + return ret; +} +EXPORT_SYMBOL(mcde_dss_get_default_pixel_format); + +void mcde_dss_get_physical_size(struct mcde_display_device *ddev, + u16 *physical_width, u16 *physical_height) +{ + mutex_lock(&ddev->display_lock); + ddev->get_physical_size(ddev, physical_width, physical_height); + mutex_unlock(&ddev->display_lock); +} +EXPORT_SYMBOL(mcde_dss_get_physical_size); + +int mcde_dss_try_video_mode(struct mcde_display_device *ddev, + struct mcde_video_mode *video_mode) +{ + int ret; + mutex_lock(&ddev->display_lock); + ret = ddev->try_video_mode(ddev, video_mode); + mutex_unlock(&ddev->display_lock); + return ret; +} +EXPORT_SYMBOL(mcde_dss_try_video_mode); + +int mcde_dss_set_video_mode(struct mcde_display_device *ddev, + struct mcde_video_mode *vmode) +{ + int ret = 0; + struct mcde_video_mode old_vmode; + + mutex_lock(&ddev->display_lock); + /* Do not perform set_video_mode if power mode is off */ + if (ddev->get_power_mode(ddev) == MCDE_DISPLAY_PM_OFF) + goto power_mode_off; + + ddev->get_video_mode(ddev, &old_vmode); + if (memcmp(vmode, &old_vmode, sizeof(old_vmode)) == 0) + goto same_video_mode; + + ret = ddev->set_video_mode(ddev, vmode); + if (ret) + goto set_video_mode_failed; + + if (ddev->invalidate_area) + ret = ddev->invalidate_area(ddev, NULL); +power_mode_off: +same_video_mode: +set_video_mode_failed: + mutex_unlock(&ddev->display_lock); + return ret; +} +EXPORT_SYMBOL(mcde_dss_set_video_mode); + +void mcde_dss_get_video_mode(struct mcde_display_device *ddev, + struct mcde_video_mode *video_mode) +{ + mutex_lock(&ddev->display_lock); + ddev->get_video_mode(ddev, video_mode); + mutex_unlock(&ddev->display_lock); +} +EXPORT_SYMBOL(mcde_dss_get_video_mode); + +int mcde_dss_set_pixel_format(struct mcde_display_device *ddev, + enum mcde_ovly_pix_fmt pix_fmt) +{ + enum mcde_ovly_pix_fmt old_pix_fmt; + int ret; + + mutex_lock(&ddev->display_lock); + old_pix_fmt = ddev->get_pixel_format(ddev); + if (old_pix_fmt == pix_fmt) { + ret = 0; + goto same_pixel_format; + } + + ret = ddev->set_pixel_format(ddev, pix_fmt); + +same_pixel_format: + mutex_unlock(&ddev->display_lock); + return ret; +} +EXPORT_SYMBOL(mcde_dss_set_pixel_format); + +int mcde_dss_get_pixel_format(struct mcde_display_device *ddev) +{ + int ret; + mutex_lock(&ddev->display_lock); + ret = ddev->get_pixel_format(ddev); + mutex_unlock(&ddev->display_lock); + return ret; +} +EXPORT_SYMBOL(mcde_dss_get_pixel_format); + +int mcde_dss_set_rotation(struct mcde_display_device *ddev, + enum mcde_display_rotation rotation) +{ + int ret; + enum mcde_display_rotation old_rotation; + + mutex_lock(&ddev->display_lock); + old_rotation = ddev->get_rotation(ddev); + if (old_rotation == rotation) { + ret = 0; + goto same_rotation; + } + + ret = ddev->set_rotation(ddev, rotation); +same_rotation: + mutex_unlock(&ddev->display_lock); + return ret; +} +EXPORT_SYMBOL(mcde_dss_set_rotation); + +enum mcde_display_rotation mcde_dss_get_rotation( + struct mcde_display_device *ddev) +{ + int ret; + mutex_lock(&ddev->display_lock); + ret = ddev->get_rotation(ddev); + mutex_unlock(&ddev->display_lock); + return ret; +} +EXPORT_SYMBOL(mcde_dss_get_rotation); + +int __init mcde_dss_init(void) +{ + return 0; +} + +void mcde_dss_exit(void) +{ +} + |