237 lines
6.4 KiB
C
237 lines
6.4 KiB
C
/* drivers/video/msm_fb/mddi_client_simple.c
|
|
*
|
|
* Support for simple mddi client devices which require no special
|
|
* initialization code except for what may be provided in the board file.
|
|
* If the clients do not provide board specific code, this driver's
|
|
* panel operations are no-ops.
|
|
*
|
|
* Copyright (C) 2007-2010, Google Incorporated
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* 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/interrupt.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/wait.h>
|
|
|
|
#include <mach/msm_fb.h>
|
|
|
|
struct panel_info {
|
|
struct platform_device pdev;
|
|
struct msm_mddi_client_data *client_data;
|
|
struct msm_panel_data panel_data;
|
|
struct msmfb_callback *fb_callback;
|
|
wait_queue_head_t vsync_wait;
|
|
int got_vsync;
|
|
int irq;
|
|
};
|
|
|
|
#define to_panel_info(pd) container_of((pd), struct panel_info, panel_data)
|
|
|
|
static void mddi_simple_request_vsync(struct msm_panel_data *panel_data,
|
|
struct msmfb_callback *callback)
|
|
{
|
|
struct panel_info *panel = to_panel_info(panel_data);
|
|
struct msm_mddi_client_data *client_data = panel->client_data;
|
|
|
|
panel->fb_callback = callback;
|
|
if (panel->got_vsync) {
|
|
panel->got_vsync = 0;
|
|
client_data->activate_link(client_data); /* clears interrupt */
|
|
}
|
|
}
|
|
|
|
static void mddi_simple_wait_vsync(struct msm_panel_data *panel_data)
|
|
{
|
|
struct panel_info *panel = to_panel_info(panel_data);
|
|
struct msm_mddi_client_data *client_data = panel->client_data;
|
|
int ret;
|
|
|
|
if (panel->got_vsync) {
|
|
panel->got_vsync = 0;
|
|
client_data->activate_link(client_data); /* clears interrupt */
|
|
}
|
|
|
|
ret = wait_event_timeout(panel->vsync_wait, panel->got_vsync, HZ/2);
|
|
if (!ret && !panel->got_vsync)
|
|
pr_err("mddi_client_simple: timeout waiting for vsync\n");
|
|
|
|
panel->got_vsync = 0;
|
|
/* interrupt clears when screen dma starts */
|
|
}
|
|
|
|
|
|
static int mddi_simple_suspend(struct msm_panel_data *panel_data)
|
|
{
|
|
struct panel_info *panel = to_panel_info(panel_data);
|
|
struct msm_mddi_client_data *client_data = panel->client_data;
|
|
struct msm_mddi_bridge_platform_data *bridge_data =
|
|
client_data->private_client_data;
|
|
int ret;
|
|
|
|
if (!bridge_data->uninit)
|
|
return 0;
|
|
|
|
ret = bridge_data->uninit(bridge_data, client_data);
|
|
if (ret) {
|
|
pr_info("%s: non zero return from uninit\n", __func__);
|
|
return ret;
|
|
}
|
|
client_data->suspend(client_data);
|
|
return 0;
|
|
}
|
|
|
|
static int mddi_simple_resume(struct msm_panel_data *panel_data)
|
|
{
|
|
struct panel_info *panel = to_panel_info(panel_data);
|
|
struct msm_mddi_client_data *client_data = panel->client_data;
|
|
struct msm_mddi_bridge_platform_data *bridge_data =
|
|
client_data->private_client_data;
|
|
|
|
if (!bridge_data->init)
|
|
return 0;
|
|
|
|
client_data->resume(client_data);
|
|
return bridge_data->init(bridge_data, client_data);
|
|
}
|
|
|
|
static int mddi_simple_blank(struct msm_panel_data *panel_data)
|
|
{
|
|
struct panel_info *panel = to_panel_info(panel_data);
|
|
struct msm_mddi_client_data *client_data = panel->client_data;
|
|
struct msm_mddi_bridge_platform_data *bridge_data =
|
|
client_data->private_client_data;
|
|
|
|
if (!bridge_data->blank)
|
|
return 0;
|
|
return bridge_data->blank(bridge_data, client_data);
|
|
}
|
|
|
|
static int mddi_simple_unblank(struct msm_panel_data *panel_data)
|
|
{
|
|
struct panel_info *panel = to_panel_info(panel_data);
|
|
struct msm_mddi_client_data *client_data = panel->client_data;
|
|
struct msm_mddi_bridge_platform_data *bridge_data =
|
|
client_data->private_client_data;
|
|
|
|
if (!bridge_data->unblank)
|
|
return 0;
|
|
return bridge_data->unblank(bridge_data, client_data);
|
|
}
|
|
|
|
static irqreturn_t handle_vsync_irq(int irq, void *data)
|
|
{
|
|
struct panel_info *panel = data;
|
|
|
|
panel->got_vsync = 1;
|
|
if (panel->fb_callback) {
|
|
panel->fb_callback->func(panel->fb_callback);
|
|
panel->fb_callback = NULL;
|
|
}
|
|
|
|
wake_up(&panel->vsync_wait);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int mddi_simple_probe(struct platform_device *pdev)
|
|
{
|
|
struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
|
|
struct msm_mddi_bridge_platform_data *bridge_data =
|
|
client_data->private_client_data;
|
|
struct panel_info *panel;
|
|
int ret;
|
|
|
|
pr_debug("%s()\n", __func__);
|
|
|
|
panel = kzalloc(sizeof(struct panel_info), GFP_KERNEL);
|
|
if (!panel)
|
|
return -ENOMEM;
|
|
|
|
platform_set_drvdata(pdev, panel);
|
|
|
|
init_waitqueue_head(&panel->vsync_wait);
|
|
|
|
panel->irq = platform_get_irq_byname(pdev, "vsync");
|
|
if (panel->irq >= 0) {
|
|
ret = request_irq(panel->irq, handle_vsync_irq,
|
|
IRQF_TRIGGER_RISING, "mddi_c_simple_vsync",
|
|
panel);
|
|
if (ret) {
|
|
pr_err("%s: request vsync irq %d failed (%d)\n",
|
|
__func__, panel->irq, ret);
|
|
goto err_req_irq;
|
|
}
|
|
|
|
panel->panel_data.wait_vsync = mddi_simple_wait_vsync;
|
|
panel->panel_data.request_vsync = mddi_simple_request_vsync;
|
|
}
|
|
|
|
panel->client_data = client_data;
|
|
panel->panel_data.suspend = mddi_simple_suspend;
|
|
panel->panel_data.resume = mddi_simple_resume;
|
|
panel->panel_data.blank = mddi_simple_blank;
|
|
panel->panel_data.unblank = mddi_simple_unblank;
|
|
panel->panel_data.caps = bridge_data->caps;
|
|
panel->panel_data.fb_data = &bridge_data->fb_data;
|
|
|
|
panel->pdev.name = "msm_panel";
|
|
panel->pdev.id = pdev->id;
|
|
platform_device_add_resources(&panel->pdev,
|
|
client_data->fb_resource, 1);
|
|
panel->pdev.dev.platform_data = &panel->panel_data;
|
|
|
|
if (bridge_data->init)
|
|
bridge_data->init(bridge_data, client_data);
|
|
|
|
ret = platform_device_register(&panel->pdev);
|
|
if (ret) {
|
|
pr_err("%s: Can't register platform device\n", __func__);
|
|
goto err_plat_dev_reg;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_plat_dev_reg:
|
|
if (panel->irq >= 0)
|
|
free_irq(panel->irq, panel);
|
|
err_req_irq:
|
|
platform_set_drvdata(pdev, NULL);
|
|
kfree(panel);
|
|
return ret;
|
|
}
|
|
|
|
static int mddi_simple_remove(struct platform_device *pdev)
|
|
{
|
|
struct panel_info *panel = platform_get_drvdata(pdev);
|
|
kfree(panel);
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver mddi_client_simple = {
|
|
.probe = mddi_simple_probe,
|
|
.remove = mddi_simple_remove,
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = "mddi_c_simple"
|
|
},
|
|
};
|
|
|
|
static int __init mddi_client_simple_init(void)
|
|
{
|
|
platform_driver_register(&mddi_client_simple);
|
|
return 0;
|
|
}
|
|
|
|
module_init(mddi_client_simple_init);
|