android_kernel_google_msm/drivers/video/msm/mdp4_overlay_writeback.c
Deva Ramasubramanian 06e5e6ec87 msm: mdp: Move wfd state signalling into mdp driver
Previously the WFD driver was reponsible for signalling the
online/offline events to userspace listeners.  This led to redundant
notifications due to the manner in which the WFD driver is used. The
signalling is better purposed when done from MDP driver.

Change-Id: Ifffbacb565f8758b65139f7b0e32df0773247e2d
Signed-off-by: Deva Ramasubramanian <dramasub@codeaurora.org>
2013-03-15 17:08:48 -07:00

882 lines
21 KiB
C

/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/hrtimer.h>
#include <linux/delay.h>
#include <mach/hardware.h>
#include <linux/io.h>
#include <mach/iommu_domains.h>
#include <asm/system.h>
#include <asm/mach-types.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
#include <linux/fb.h>
#include "mdp.h"
#include "msm_fb.h"
#include "mdp4.h"
enum {
WB_OPEN,
WB_START,
WB_STOPING,
WB_STOP
};
enum {
REGISTERED,
IN_FREE_QUEUE,
IN_BUSY_QUEUE,
WITH_CLIENT
};
#define MAX_CONTROLLER 1
#define VSYNC_EXPIRE_TICK 0
static struct vsycn_ctrl {
struct device *dev;
int inited;
int update_ndx;
u32 ov_koff;
u32 ov_done;
atomic_t suspend;
struct mutex update_lock;
struct completion ov_comp;
spinlock_t spin_lock;
struct msm_fb_data_type *mfd;
struct mdp4_overlay_pipe *base_pipe;
struct vsync_update vlist[2];
struct work_struct clk_work;
} vsync_ctrl_db[MAX_CONTROLLER];
static void vsync_irq_enable(int intr, int term)
{
unsigned long flag;
spin_lock_irqsave(&mdp_spin_lock, flag);
/* no need to clrear other interrupts for comamnd mode */
mdp_intr_mask |= intr;
outp32(MDP_INTR_ENABLE, mdp_intr_mask);
mdp_enable_irq(term);
spin_unlock_irqrestore(&mdp_spin_lock, flag);
}
static void vsync_irq_disable(int intr, int term)
{
unsigned long flag;
spin_lock_irqsave(&mdp_spin_lock, flag);
/* no need to clrear other interrupts for comamnd mode */
mdp_intr_mask &= ~intr;
outp32(MDP_INTR_ENABLE, mdp_intr_mask);
mdp_disable_irq_nosync(term);
spin_unlock_irqrestore(&mdp_spin_lock, flag);
}
static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd);
static void mdp4_wfd_queue_wakeup(struct msm_fb_data_type *mfd,
struct msmfb_writeback_data_list *node);
static int mdp4_wfd_dequeue_update(struct msm_fb_data_type *mfd,
struct msmfb_writeback_data_list **wfdnode);
int mdp4_overlay_writeback_on(struct platform_device *pdev)
{
struct msm_fb_data_type *mfd;
struct fb_info *fbi;
uint8 *buf;
struct mdp4_overlay_pipe *pipe;
int bpp;
int ret;
uint32 data;
struct vsycn_ctrl *vctrl;
int cndx = 0;
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
if (!mfd)
return -ENODEV;
if (mfd->key != MFD_KEY)
return -EINVAL;
vctrl = &vsync_ctrl_db[cndx];
vctrl->mfd = mfd;
vctrl->dev = mfd->fbi->dev;
fbi = mfd->fbi;
bpp = fbi->var.bits_per_pixel / 8;
buf = (uint8 *) fbi->fix.smem_start;
buf += fbi->var.xoffset * bpp +
fbi->var.yoffset * fbi->fix.line_length;
/* MDP cmd block enable */
mdp_clk_ctrl(1);
if (vctrl->base_pipe == NULL) {
pipe = mdp4_overlay_pipe_alloc(OVERLAY_TYPE_BF, MDP4_MIXER2);
if (pipe == NULL) {
pr_info("%s: pipe_alloc failed\n", __func__);
mdp_clk_ctrl(0);
return -EIO;
}
pipe->pipe_used++;
pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
pipe->mixer_num = MDP4_MIXER2;
pipe->src_format = MDP_ARGB_8888;
mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_WRITEBACK);
ret = mdp4_overlay_format2pipe(pipe);
if (ret < 0)
pr_info("%s: format2type failed\n", __func__);
vctrl->base_pipe = pipe; /* keep it */
} else {
pipe = vctrl->base_pipe;
}
ret = panel_next_on(pdev);
/* MDP_LAYERMIXER_WB_MUX_SEL to use mixer1 axi for mixer2 writeback */
if (hdmi_prim_display)
data = 0x01;
else
data = 0x02;
outpdw(MDP_BASE + 0x100F4, data);
MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5004,
((0x0 & 0xFFF) << 16) | /* 12-bit B */
(0x0 & 0xFFF)); /* 12-bit G */
/* MSP_BORDER_COLOR */
MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5008,
(0x0 & 0xFFF)); /* 12-bit R */
mdp_clk_ctrl(0);
atomic_set(&vctrl->suspend, 0);
return ret;
}
static void mdp4_wfd_wait4ov(int cndx);
static void mdp4_writeback_pipe_clean(struct vsync_update *vp);
int mdp4_overlay_writeback_off(struct platform_device *pdev)
{
int cndx = 0;
struct msm_fb_data_type *mfd;
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
int ret = 0;
int undx;
struct vsync_update *vp;
pr_debug("%s+:\n", __func__);
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
atomic_set(&vctrl->suspend, 1);
if (pipe == NULL) {
pr_err("%s: NO base pipe\n", __func__);
return ret;
}
complete(&vctrl->ov_comp);
msleep(20);
mdp_clk_ctrl(1);
/* sanity check, free pipes besides base layer */
mdp4_overlay_unset_mixer(pipe->mixer_num);
mdp4_mixer_stage_down(pipe, 1);
mdp4_overlay_pipe_free(pipe);
vctrl->base_pipe = NULL;
mdp_clk_ctrl(0);
undx = vctrl->update_ndx;
vp = &vctrl->vlist[undx];
if (vp->update_cnt) {
/*
* pipe's iommu will be freed at next overlay play
* and iommu_drop statistic will be increased by one
*/
pr_warn("%s: update_cnt=%d\n", __func__, vp->update_cnt);
mdp4_writeback_pipe_clean(vp);
}
ret = panel_next_off(pdev);
mdp_clk_ctrl(1);
/* MDP_LAYERMIXER_WB_MUX_SEL to restore to default cfg*/
outpdw(MDP_BASE + 0x100F4, 0x0);
mdp_clk_ctrl(0);
pr_debug("%s-:\n", __func__);
return ret;
}
static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd)
{
struct fb_info *fbi;
uint8 *buf;
unsigned int buf_offset;
struct mdp4_overlay_pipe *pipe;
int bpp;
int cndx = 0;
struct vsycn_ctrl *vctrl;
if (mfd->key != MFD_KEY)
return -ENODEV;
fbi = mfd->fbi;
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
if (!pipe) {
pr_err("%s: no base layer pipe\n", __func__);
return -EINVAL;
}
bpp = fbi->var.bits_per_pixel / 8;
buf = (uint8 *) fbi->fix.smem_start;
buf_offset = fbi->var.xoffset * bpp +
fbi->var.yoffset * fbi->fix.line_length;
/* MDP cmd block enable */
mdp_clk_ctrl(1);
pipe->src_height = fbi->var.yres;
pipe->src_width = fbi->var.xres;
pipe->src_h = fbi->var.yres;
pipe->src_w = fbi->var.xres;
pipe->dst_h = fbi->var.yres;
pipe->dst_w = fbi->var.xres;
pipe->srcp0_ystride = fbi->fix.line_length;
pipe->src_y = 0;
pipe->src_x = 0;
pipe->dst_y = 0;
pipe->dst_x = 0;
mdp4_overlay_mdp_pipe_req(pipe, mfd);
mdp4_calc_blt_mdp_bw(mfd, pipe);
if (mfd->display_iova)
pipe->srcp0_addr = mfd->display_iova + buf_offset;
else
pipe->srcp0_addr = (uint32)(buf + buf_offset);
mdp4_mixer_stage_up(pipe, 0);
mdp4_overlayproc_cfg(pipe);
if (hdmi_prim_display)
outpdw(MDP_BASE + 0x100F4, 0x01);
else
outpdw(MDP_BASE + 0x100F4, 0x02);
/* MDP cmd block disable */
mdp_clk_ctrl(0);
wmb();
return 0;
}
/*
* mdp4_wfd_piep_queue:
* called from thread context
*/
void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)
{
struct vsycn_ctrl *vctrl;
struct vsync_update *vp;
struct mdp4_overlay_pipe *pp;
int undx;
if (cndx >= MAX_CONTROLLER) {
pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
return;
}
vctrl = &vsync_ctrl_db[cndx];
if (atomic_read(&vctrl->suspend) > 0)
return;
mutex_lock(&vctrl->update_lock);
undx = vctrl->update_ndx;
vp = &vctrl->vlist[undx];
pp = &vp->plist[pipe->pipe_ndx - 1]; /* ndx start form 1 */
pr_debug("%s: vndx=%d pipe_ndx=%d pid=%d\n", __func__,
undx, pipe->pipe_ndx, current->pid);
*pp = *pipe; /* clone it */
vp->update_cnt++;
mutex_unlock(&vctrl->update_lock);
mdp4_stat.overlay_play[pipe->mixer_num]++;
}
static void mdp4_writeback_pipe_clean(struct vsync_update *vp)
{
struct mdp4_overlay_pipe *pipe;
int i;
pipe = vp->plist;
for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
if (pipe->pipe_used) {
mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
pipe->pipe_used = 0; /* clear */
}
}
vp->update_cnt = 0; /* empty queue */
}
int mdp4_wfd_pipe_commit(struct msm_fb_data_type *mfd,
int cndx, int wait)
{
int i, undx;
int mixer = 0;
struct vsycn_ctrl *vctrl;
struct vsync_update *vp;
struct mdp4_overlay_pipe *pipe;
struct mdp4_overlay_pipe *real_pipe;
unsigned long flags;
int cnt = 0;
struct msmfb_writeback_data_list *node = NULL;
int rc = 0;
vctrl = &vsync_ctrl_db[cndx];
mutex_lock(&vctrl->update_lock);
undx = vctrl->update_ndx;
vp = &vctrl->vlist[undx];
pipe = vctrl->base_pipe;
mixer = pipe->mixer_num;
/*
* allow stage_commit without pipes queued
* (vp->update_cnt == 0) to unstage pipes after
* overlay_unset
*/
vctrl->update_ndx++;
vctrl->update_ndx &= 0x01;
vp->update_cnt = 0; /* reset */
mutex_unlock(&vctrl->update_lock);
rc = mdp4_wfd_dequeue_update(mfd, &node);
if (rc != 0) {
pr_err("%s: mdp4_wfd_dequeue_update failed !! mfd=%x\n",
__func__, (int)mfd);
pipe = vp->plist;
for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
pipe->pipe_used = 0;
pr_info("%s: dequeue update failed, unsetting pipes\n",
__func__);
}
return cnt;
}
/* free previous committed iommu back to pool */
mdp4_overlay_iommu_unmap_freelist(mixer);
mdp_clk_ctrl(1);
pipe = vp->plist;
for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
if (pipe->pipe_used) {
cnt++;
real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx);
if (real_pipe && real_pipe->pipe_used) {
/* pipe not unset */
mdp4_overlay_vsync_commit(pipe);
}
/* free previous iommu to freelist
* which will be freed at next
* pipe_commit
*/
mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
pipe->pipe_used = 0; /* clear */
}
}
mdp_clk_ctrl(1);
mdp4_mixer_stage_commit(mixer);
pipe = vctrl->base_pipe;
spin_lock_irqsave(&vctrl->spin_lock, flags);
vctrl->ov_koff++;
INIT_COMPLETION(vctrl->ov_comp);
vsync_irq_enable(INTR_OVERLAY2_DONE, MDP_OVERLAY2_TERM);
pr_debug("%s: kickoff\n", __func__);
/* kickoff overlay engine */
mdp4_stat.kickoff_ov2++;
outpdw(MDP_BASE + 0x00D0, 0);
mb(); /* make sure kickoff executed */
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
mdp4_stat.overlay_commit[pipe->mixer_num]++;
if (wait)
mdp4_wfd_wait4ov(cndx);
mdp4_wfd_queue_wakeup(mfd, node);
return cnt;
}
static void clk_ctrl_work(struct work_struct *work)
{
struct vsycn_ctrl *vctrl =
container_of(work, typeof(*vctrl), clk_work);
mdp_clk_ctrl(0);
}
void mdp4_wfd_init(int cndx)
{
struct vsycn_ctrl *vctrl;
if (cndx >= MAX_CONTROLLER) {
pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
return;
}
vctrl = &vsync_ctrl_db[cndx];
if (vctrl->inited)
return;
vctrl->inited = 1;
vctrl->update_ndx = 0;
mutex_init(&vctrl->update_lock);
init_completion(&vctrl->ov_comp);
atomic_set(&vctrl->suspend, 1);
spin_lock_init(&vctrl->spin_lock);
INIT_WORK(&vctrl->clk_work, clk_ctrl_work);
}
static void mdp4_wfd_wait4ov(int cndx)
{
struct vsycn_ctrl *vctrl;
if (cndx >= MAX_CONTROLLER) {
pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
return;
}
vctrl = &vsync_ctrl_db[cndx];
if (atomic_read(&vctrl->suspend) > 0)
return;
wait_for_completion(&vctrl->ov_comp);
}
void mdp4_overlay2_done_wfd(struct mdp_dma_data *dma)
{
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
int cndx = 0;
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
spin_lock(&vctrl->spin_lock);
vsync_irq_disable(INTR_OVERLAY2_DONE, MDP_OVERLAY2_TERM);
vctrl->ov_done++;
complete(&vctrl->ov_comp);
schedule_work(&vctrl->clk_work);
pr_debug("%s ovdone interrupt\n", __func__);
spin_unlock(&vctrl->spin_lock);
}
void mdp4_writeback_overlay(struct msm_fb_data_type *mfd)
{
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
if (mfd && !mfd->panel_power_on)
return;
pr_debug("%s:+ mfd=%x\n", __func__, (int)mfd);
vctrl = &vsync_ctrl_db[0];
pipe = vctrl->base_pipe;
mutex_lock(&mfd->dma->ov_mutex);
if (pipe->pipe_type == OVERLAY_TYPE_RGB)
mdp4_wfd_pipe_queue(0, pipe);
mdp4_overlay_mdp_perf_upd(mfd, 1);
mdp4_wfd_pipe_commit(mfd, 0, 1);
mdp4_overlay_mdp_perf_upd(mfd, 0);
mutex_unlock(&mfd->dma->ov_mutex);
}
static int mdp4_overlay_writeback_register_buffer(
struct msm_fb_data_type *mfd, struct msmfb_writeback_data_list *node)
{
if (!node) {
pr_err("Cannot register a NULL node\n");
return -EINVAL;
}
node->state = REGISTERED;
list_add_tail(&node->registered_entry, &mfd->writeback_register_queue);
return 0;
}
static struct msmfb_writeback_data_list *get_if_registered(
struct msm_fb_data_type *mfd, struct msmfb_data *data)
{
struct msmfb_writeback_data_list *temp;
bool found = false;
int domain;
if (!list_empty(&mfd->writeback_register_queue)) {
list_for_each_entry(temp,
&mfd->writeback_register_queue,
registered_entry) {
if (temp && temp->buf_info.iova == data->iova) {
found = true;
break;
}
}
}
if (!found) {
temp = kzalloc(sizeof(struct msmfb_writeback_data_list),
GFP_KERNEL);
if (temp == NULL) {
pr_err("%s: out of memory\n", __func__);
goto register_alloc_fail;
}
temp->ihdl = NULL;
if (data->iova)
temp->addr = (void *)(data->iova + data->offset);
else if (mfd->iclient) {
struct ion_handle *srcp_ihdl;
ulong len;
srcp_ihdl = ion_import_dma_buf(mfd->iclient,
data->memory_id);
if (IS_ERR_OR_NULL(srcp_ihdl)) {
pr_err("%s: ion import fd failed\n", __func__);
goto register_ion_fail;
}
if (mdp_iommu_split_domain)
domain = DISPLAY_WRITE_DOMAIN;
else
domain = DISPLAY_READ_DOMAIN;
if (ion_map_iommu(mfd->iclient,
srcp_ihdl,
domain,
GEN_POOL,
SZ_4K,
0,
(ulong *)&temp->addr,
(ulong *)&len,
0,
ION_IOMMU_UNMAP_DELAYED)) {
ion_free(mfd->iclient, srcp_ihdl);
pr_err("%s: unable to get ion mapping addr\n",
__func__);
goto register_ion_fail;
}
temp->addr += data->offset;
temp->ihdl = srcp_ihdl;
}
else {
pr_err("%s: only support ion memory\n", __func__);
goto register_ion_fail;
}
memcpy(&temp->buf_info, data, sizeof(struct msmfb_data));
if (mdp4_overlay_writeback_register_buffer(mfd, temp)) {
pr_err("%s: error registering node\n", __func__);
goto register_ion_fail;
}
}
return temp;
register_ion_fail:
kfree(temp);
register_alloc_fail:
return NULL;
}
int mdp4_writeback_start(
struct fb_info *info)
{
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
mutex_lock(&mfd->writeback_mutex);
mfd->writeback_state = WB_START;
mutex_unlock(&mfd->writeback_mutex);
wake_up(&mfd->wait_q);
return 0;
}
int mdp4_writeback_queue_buffer(struct fb_info *info, struct msmfb_data *data)
{
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
struct msmfb_writeback_data_list *node = NULL;
int rv = 0;
mutex_lock(&mfd->writeback_mutex);
node = get_if_registered(mfd, data);
if (!node || node->state == IN_BUSY_QUEUE ||
node->state == IN_FREE_QUEUE) {
pr_err("memory not registered or Buffer already with us\n");
rv = -EINVAL;
goto exit;
}
list_add_tail(&node->active_entry, &mfd->writeback_free_queue);
node->state = IN_FREE_QUEUE;
exit:
mutex_unlock(&mfd->writeback_mutex);
return rv;
}
static int is_buffer_ready(struct msm_fb_data_type *mfd)
{
int rc;
mutex_lock(&mfd->writeback_mutex);
rc = !list_empty(&mfd->writeback_busy_queue) ||
(mfd->writeback_state == WB_STOPING);
mutex_unlock(&mfd->writeback_mutex);
return rc;
}
int mdp4_writeback_dequeue_buffer(struct fb_info *info, struct msmfb_data *data)
{
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
struct msmfb_writeback_data_list *node = NULL;
int rc = 0, domain;
rc = wait_event_interruptible(mfd->wait_q, is_buffer_ready(mfd));
if (rc) {
pr_err("failed to get dequeued buffer\n");
return -ENOBUFS;
}
mutex_lock(&mfd->writeback_mutex);
if (mfd->writeback_state == WB_STOPING) {
mfd->writeback_state = WB_STOP;
mutex_unlock(&mfd->writeback_mutex);
return -ENOBUFS;
} else if (!list_empty(&mfd->writeback_busy_queue)) {
node = list_first_entry(&mfd->writeback_busy_queue,
struct msmfb_writeback_data_list, active_entry);
}
if (node) {
list_del(&node->active_entry);
node->state = WITH_CLIENT;
memcpy(data, &node->buf_info, sizeof(struct msmfb_data));
if (!data->iova)
if (mfd->iclient && node->ihdl) {
if (mdp_iommu_split_domain)
domain = DISPLAY_WRITE_DOMAIN;
else
domain = DISPLAY_READ_DOMAIN;
ion_unmap_iommu(mfd->iclient,
node->ihdl,
domain,
GEN_POOL);
ion_free(mfd->iclient,
node->ihdl);
}
} else {
pr_err("node is NULL. Somebody else dequeued?\n");
rc = -ENOBUFS;
}
mutex_unlock(&mfd->writeback_mutex);
return rc;
}
static bool is_writeback_inactive(struct msm_fb_data_type *mfd)
{
bool active;
mutex_lock(&mfd->writeback_mutex);
active = !mfd->writeback_active_cnt;
mutex_unlock(&mfd->writeback_mutex);
return active;
}
int mdp4_writeback_stop(struct fb_info *info)
{
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
mutex_lock(&mfd->writeback_mutex);
mfd->writeback_state = WB_STOPING;
mutex_unlock(&mfd->writeback_mutex);
/* Wait for all pending writebacks to finish */
wait_event_interruptible(mfd->wait_q, is_writeback_inactive(mfd));
/* Wake up dequeue thread in case of no UI update*/
wake_up(&mfd->wait_q);
return 0;
}
int mdp4_writeback_init(struct fb_info *info)
{
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
mutex_init(&mfd->writeback_mutex);
mutex_init(&mfd->unregister_mutex);
INIT_LIST_HEAD(&mfd->writeback_free_queue);
INIT_LIST_HEAD(&mfd->writeback_busy_queue);
INIT_LIST_HEAD(&mfd->writeback_register_queue);
mfd->writeback_state = WB_OPEN;
init_waitqueue_head(&mfd->wait_q);
return 0;
}
int mdp4_writeback_terminate(struct fb_info *info)
{
struct list_head *ptr, *next;
struct msmfb_writeback_data_list *temp;
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
int rc = 0;
mutex_lock(&mfd->unregister_mutex);
mutex_lock(&mfd->writeback_mutex);
if (mfd->writeback_state != WB_STOPING &&
mfd->writeback_state != WB_STOP) {
pr_err("%s called without stopping\n", __func__);
rc = -EPERM;
goto terminate_err;
}
if (!list_empty(&mfd->writeback_register_queue)) {
list_for_each_safe(ptr, next,
&mfd->writeback_register_queue) {
temp = list_entry(ptr,
struct msmfb_writeback_data_list,
registered_entry);
list_del(&temp->registered_entry);
kfree(temp);
}
}
INIT_LIST_HEAD(&mfd->writeback_register_queue);
INIT_LIST_HEAD(&mfd->writeback_busy_queue);
INIT_LIST_HEAD(&mfd->writeback_free_queue);
terminate_err:
mutex_unlock(&mfd->writeback_mutex);
mutex_unlock(&mfd->unregister_mutex);
return rc;
}
static int mdp4_wfd_dequeue_update(struct msm_fb_data_type *mfd,
struct msmfb_writeback_data_list **wfdnode)
{
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
struct msmfb_writeback_data_list *node = NULL;
if (mfd && !mfd->panel_power_on)
return -EPERM;
pr_debug("%s:+ mfd=%x\n", __func__, (int)mfd);
vctrl = &vsync_ctrl_db[0];
pipe = vctrl->base_pipe;
mutex_lock(&mfd->unregister_mutex);
mutex_lock(&mfd->writeback_mutex);
if (!list_empty(&mfd->writeback_free_queue)
&& mfd->writeback_state != WB_STOPING
&& mfd->writeback_state != WB_STOP) {
node = list_first_entry(&mfd->writeback_free_queue,
struct msmfb_writeback_data_list, active_entry);
}
if (node) {
list_del(&(node->active_entry));
node->state = IN_BUSY_QUEUE;
mfd->writeback_active_cnt++;
}
mutex_unlock(&mfd->writeback_mutex);
pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
if (!pipe->ov_blt_addr) {
pr_err("%s: no writeback buffer 0x%x, %p\n", __func__,
(unsigned int)pipe->ov_blt_addr, node);
if (node) {
mutex_lock(&mfd->writeback_mutex);
list_add_tail(&node->active_entry,
&mfd->writeback_free_queue);
node->state = IN_FREE_QUEUE;
mfd->writeback_active_cnt--;
mutex_unlock(&mfd->writeback_mutex);
}
mutex_unlock(&mfd->unregister_mutex);
return -EINVAL;
}
mdp4_overlay_writeback_update(mfd);
*wfdnode = node;
mutex_unlock(&mfd->unregister_mutex);
return 0;
}
static void mdp4_wfd_queue_wakeup(struct msm_fb_data_type *mfd,
struct msmfb_writeback_data_list *node)
{
if (mfd && !mfd->panel_power_on)
return;
if (node == NULL)
return;
pr_debug("%s: mfd=%x node: %p", __func__, (int)mfd, node);
mutex_lock(&mfd->writeback_mutex);
list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
mfd->writeback_active_cnt--;
mutex_unlock(&mfd->writeback_mutex);
wake_up(&mfd->wait_q);
}
int mdp4_writeback_set_mirroring_hint(struct fb_info *info, int hint)
{
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
if (mfd->panel.type != WRITEBACK_PANEL)
return -ENOTSUPP;
switch (hint) {
case MDP_WRITEBACK_MIRROR_ON:
case MDP_WRITEBACK_MIRROR_PAUSE:
case MDP_WRITEBACK_MIRROR_RESUME:
case MDP_WRITEBACK_MIRROR_OFF:
pr_info("wfd state switched to %d\n", hint);
switch_set_state(&mfd->writeback_sdev, hint);
return 0;
default:
return -EINVAL;
}
}