Commit d13970fd authored by Juha Yrjola's avatar Juha Yrjola

ARM: OMAP: Add external LCD controller support for DSP Gateway

If an external LCD controller is in manual update mode, the DSP
must let the FB driver know when a frame should be send over the
remote framebuffer interface.

Original code by Toshihiro, FB parts by Imre.
Signed-off-by: default avatarImre Deak <imre.deak@nokia.com>
Signed-off-by: default avatarJuha Yrjl <juha.yrjola@nokia.com>
parent c5bf6988
......@@ -437,11 +437,15 @@ static DECLARE_WORK(mbx1_work, (void (*)(void *))do_mbx1, NULL);
/*
* kernel function dispatcher
*/
extern void mbx1_fbctl_upd(void);
extern void mbx1_fbctl_disable(void);
static void mbx1_kfunc_fbctl(unsigned short data)
{
switch (data) {
case OMAP_DSP_MBCMD_FBCTL_UPD:
mbx1_fbctl_upd();
break;
case OMAP_DSP_MBCMD_FBCTL_DISABLE:
mbx1_fbctl_disable();
break;
......
......@@ -46,9 +46,11 @@
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
#include <asm/arch/tc.h>
#include <asm/arch/omapfb.h>
#include <asm/arch/dsp.h>
#include <asm/arch/dsp_common.h>
#include "uaccess_dsp.h"
#include "ipbuf.h"
#include "dsp.h"
#define SZ_1MB 0x100000
......@@ -116,6 +118,11 @@ struct exmap_tbl {
static struct exmap_tbl exmap_tbl[DSPMMU_TLB_LINES];
static DECLARE_RWSEM(exmap_sem);
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
static struct omapfb_notifier_block *omapfb_nb;
static int omapfb_ready;
#endif
static int dsp_exunmap(unsigned long dspadr);
static void *dspvect_page;
......@@ -1002,6 +1009,23 @@ static unsigned long unmap_free_arm(struct exmap_tbl *ent)
"omapdsp: freeing 0x%lx bytes @ adr 0x%8p\n",
size, ent->buf);
}
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
else if (ent->type == EXMAP_TYPE_FB) {
int status;
if (omapfb_nb) {
status = omapfb_unregister_client(omapfb_nb);
if (!status)
printk("omapfb_unregister_client(): "
"success\n");
else
printk("omapfb_runegister_client(): "
"failure(%d)\n", status);
kfree(omapfb_nb);
omapfb_nb = NULL;
omapfb_ready = 0;
}
}
#endif
return size;
}
......@@ -1104,14 +1128,41 @@ static void exmap_flush(void)
#error You configured OMAP_DSP_FBEXPORT, but FB was not configured!
#endif /* CONFIG_FB */
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
static int omapfb_notifier_cb(struct omapfb_notifier_block *omapfb_nb,
unsigned long event, struct omapfb_device *fbdev)
{
/* XXX */
printk("omapfb_notifier_cb(): event = %s\n",
(event == OMAPFB_EVENT_READY) ? "READY" :
(event == OMAPFB_EVENT_DISABLED) ? "DISABLED" : "Unknown");
if (event == OMAPFB_EVENT_READY)
omapfb_ready = 1;
else if (event == OMAPFB_EVENT_DISABLED)
omapfb_ready = 0;
return 0;
}
#endif
static int dsp_fbexport(unsigned long *dspadr)
{
unsigned long dspadr_actual;
unsigned long padr_sys, padr, fbsz_sys, fbsz;
int cnt;
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
int status;
#endif
printk(KERN_DEBUG "omapdsp: frame buffer export\n");
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
if (omapfb_nb) {
printk(KERN_WARNING
"omapdsp: frame buffer has been exported already!\n");
return -EBUSY;
}
#endif
if (num_registered_fb == 0) {
printk(KERN_INFO "omapdsp: frame buffer not registered.\n");
return -EINVAL;
......@@ -1169,6 +1220,21 @@ static int dsp_fbexport(unsigned long *dspadr)
/* increase the DMA priority */
set_emiff_dma_prio(15);
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
omapfb_nb = kmalloc(sizeof(struct omapfb_notifier_block), GFP_KERNEL);
if (omapfb_nb == NULL) {
printk(KERN_ERR
"omapdsp: failed to allocate memory for omapfb_nb!\n");
dsp_exunmap(dspadr_actual);
return -ENOMEM;
}
status = omapfb_register_client(omapfb_nb, omapfb_notifier_cb, NULL);
if (!status)
printk("omapfb_register_client(): success\n");
else
printk("omapfb_register_client(): failure(%d)\n", status);
#endif
return cnt;
}
......@@ -1529,6 +1595,68 @@ static int dsp_mem_release(struct inode *inode, struct file *file)
return 0;
}
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
/*
* fb update functions:
* fbupd_response() is executed by the workqueue.
* fbupd_cb() is called when fb update is done, in interrupt context.
* mbx1_fbupd() is called when KFUNC:FBCTL:UPD is received from DSP.
*/
static void fbupd_response(void *arg)
{
int status;
status = dsp_mbsend(MBCMD(KFUNC), OMAP_DSP_MBCMD_KFUNC_FBCTL,
OMAP_DSP_MBCMD_FBCTL_UPD);
if (status < 0) {
/* FIXME: DSP is busy !! */
printk(KERN_ERR
"omapdsp: DSP is busy when trying to send FBCTL:UPD "
"response!\n");
}
}
static DECLARE_WORK(fbupd_response_work, (void (*)(void *))fbupd_response,
NULL);
static void fbupd_cb(void *arg)
{
schedule_work(&fbupd_response_work);
}
void mbx1_fbctl_upd(void)
{
struct omapfb_update_window win;
volatile unsigned short *buf = ipbuf_sys_da->d;
/* FIXME: try count sometimes exceeds 1000. */
if (sync_with_dsp(&ipbuf_sys_da->s, OMAP_DSP_TID_ANON, 5000) < 0) {
printk(KERN_ERR "mbx: FBCTL:UPD - IPBUF sync failed!\n");
return;
}
win.x = buf[0];
win.y = buf[1];
win.width = buf[2];
win.height = buf[3];
win.format = buf[4];
release_ipbuf_pvt(ipbuf_sys_da);
if (!omapfb_ready) {
printk(KERN_WARNING
"omapdsp: fbupd() called while HWA742 is not ready!\n");
return;
}
//printk("calling omapfb_update_window_async()\n");
omapfb_update_window_async(&win, fbupd_cb, NULL);
}
#else /* CONFIG_FB_OMAP_LCDC_EXTERNAL */
void mbx1_fbctl_upd(void)
{
}
#endif /* CONFIG_FB_OMAP_LCDC_EXTERNAL */
/*
* sysfs files
*/
......
......@@ -34,7 +34,6 @@
/* #define OMAPFB_DBG 1 */
#include "hwa742.h"
#include "debug.h"
#define MODULE_NAME "omapfb-hwa742"
......@@ -159,13 +158,6 @@ static void hwa742_write_reg(u8 reg, u8 data)
hwa742.extif->write_data(&data, 1);
}
void hwa742_read_id(int *rev_code, int *config)
{
*rev_code = hwa742_read_reg(HWA742_REV_CODE_REG);
*config = hwa742_read_reg(HWA742_CONFIG_REG);
}
EXPORT_SYMBOL(hwa742_read_id);
static void set_window_regs(int x_start, int y_start, int x_end, int y_end)
{
u8 tmp[8];
......@@ -471,10 +463,12 @@ int hwa742_update_window_async(struct omapfb_update_window *win,
DBGENTER(2);
if (hwa742.update_mode != OMAPFB_MANUAL_UPDATE) {
DBGPRINT(1, "invalid update mode\n");
r = -EINVAL;
goto out;
}
if (unlikely(win->format & ~(0x03 | OMAPFB_FORMAT_FLAG_DOUBLE))) {
DBGPRINT(1, "invalid window flag");
r = -EINVAL;
goto out;
}
......@@ -544,37 +538,13 @@ static void hwa742_sync(void)
DBGLEAVE(2);
}
static struct notifier_block *hwa742_client_list;
int hwa742_register_client(struct hwa742_notifier_block *hwa742_nb,
hwa742_notifier_callback_t callback,
void *callback_data)
static void hwa742_bind_client(struct omapfb_notifier_block *nb)
{
int r;
DBGPRINT(1, "update_mode %d\n", hwa742.update_mode);
hwa742_nb->nb.notifier_call = (int (*)(struct notifier_block *,
unsigned long, void *))callback;
hwa742_nb->data = callback_data;
r = notifier_chain_register(&hwa742_client_list, &hwa742_nb->nb);
if (r)
return r;
if (hwa742.update_mode == OMAPFB_MANUAL_UPDATE) {
DBGPRINT(1, "calling client list\n");
notifier_call_chain(&hwa742_client_list,
HWA742_EVENT_READY,
hwa742.fbdev);
omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_READY);
}
return 0;
}
EXPORT_SYMBOL(hwa742_register_client);
int hwa742_unregister_client(struct hwa742_notifier_block *hwa742_nb)
{
return notifier_chain_unregister(&hwa742_client_list,
&hwa742_nb->nb);
}
EXPORT_SYMBOL(hwa742_unregister_client);
static int hwa742_set_update_mode(enum omapfb_update_mode mode)
{
......@@ -597,9 +567,7 @@ static int hwa742_set_update_mode(enum omapfb_update_mode mode)
switch (hwa742.update_mode) {
case OMAPFB_MANUAL_UPDATE:
notifier_call_chain(&hwa742_client_list,
HWA742_EVENT_DISABLED,
hwa742.fbdev);
omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_DISABLED);
break;
case OMAPFB_AUTO_UPDATE:
hwa742.stop_auto_update = 1;
......@@ -615,9 +583,7 @@ static int hwa742_set_update_mode(enum omapfb_update_mode mode)
switch (mode) {
case OMAPFB_MANUAL_UPDATE:
notifier_call_chain(&hwa742_client_list,
HWA742_EVENT_READY,
hwa742.fbdev);
omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_READY);
break;
case OMAPFB_AUTO_UPDATE:
hwa742_update_window_auto(0);
......@@ -915,6 +881,7 @@ struct lcd_ctrl hwa742_ctrl = {
.name = "hwa742",
.init = hwa742_init,
.cleanup = hwa742_cleanup,
.bind_client = hwa742_bind_client,
.get_caps = hwa742_get_caps,
.set_update_mode = hwa742_set_update_mode,
.get_update_mode = hwa742_get_update_mode,
......
#ifndef __HWA742_H
#define __HWA742_H
#include <linux/fb.h>
#include <asm/arch/omapfb.h>
#define HWA742_EVENT_READY 1
#define HWA742_EVENT_DISABLED 2
struct hwa742_notifier_block {
struct notifier_block nb;
void *data;
};
typedef int (*hwa742_notifier_callback_t)(struct hwa742_notifier_block *,
unsigned long event,
struct omapfb_device *fbdev);
extern void hwa742_read_id(int *rev_code, int *config);
extern int hwa742_register_client(struct hwa742_notifier_block *hwa742_nb,
hwa742_notifier_callback_t callback,
void *callback_data);
extern int hwa742_unregister_client(struct hwa742_notifier_block *hwa742_nb);
extern int hwa742_update_window_async(struct omapfb_update_window *win,
void (*complete_callback)(void *arg),
void *complete_callback_data);
#endif
......@@ -67,6 +67,10 @@ static int manual_update = 1;
static int manual_update;
#endif
static struct platform_device *fbdev_pdev;
static struct lcd_panel *fbdev_panel;
static struct omapfb_device *omapfb_dev;
static struct caps_table_struct {
unsigned long flag;
const char *name;
......@@ -576,28 +580,52 @@ static int omapfb_set_par(struct fb_info *fbi)
return r;
}
static int omapfb_update_win(struct omapfb_device *fbdev,
struct omapfb_update_window *win)
int omapfb_update_window_async(struct omapfb_update_window *win,
void (*callback)(void *),
void *callback_data)
{
struct fb_var_screeninfo *var = &fbdev->fb_info->var;
int ret;
struct omapfb_device *fbdev = omapfb_dev;
struct fb_var_screeninfo *var;
DBGENTER(2);
if (fbdev == NULL) {
DBGPRINT(1, "no fbdev\n");
return -ENODEV;
}
var = &fbdev->fb_info->var;
if (win->x >= var->xres || win->y >= var->yres)
if (win->x >= var->xres || win->y >= var->yres) {
DBGPRINT(1, "invalid x %d, y %d\n", win->x, win->y);
return -EINVAL;
}
if (!fbdev->ctrl->update_window ||
fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)
fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE) {
DBGPRINT(1, "invalid update mode\n");
return -ENODEV;
}
if (win->x + win->width >= var->xres)
win->width = var->xres - win->x;
if (win->y + win->height >= var->yres)
win->height = var->yres - win->y;
if (!win->width || !win->height)
if (!win->width || !win->height) {
DBGPRINT(1, "zero size window\n");
return 0;
}
return fbdev->ctrl->update_window(win, callback, callback_data);
}
EXPORT_SYMBOL(omapfb_update_window_async);
static int omapfb_update_win(struct omapfb_device *fbdev,
struct omapfb_update_window *win)
{
int ret;
omapfb_rqueue_lock(fbdev);
ret = fbdev->ctrl->update_window(win, NULL, 0);
ret = omapfb_update_window_async(win, NULL, 0);
omapfb_rqueue_unlock(fbdev);
return ret;
......@@ -659,6 +687,45 @@ static int omapfb_set_color_key(struct omapfb_device *fbdev,
return r;
}
static struct notifier_block *omapfb_client_list;
int omapfb_register_client(struct omapfb_notifier_block *omapfb_nb,
omapfb_notifier_callback_t callback,
void *callback_data)
{
int r;
DBGENTER(1);
omapfb_nb->nb.notifier_call = (int (*)(struct notifier_block *,
unsigned long, void *))callback;
omapfb_nb->data = callback_data;
r = notifier_chain_register(&omapfb_client_list, &omapfb_nb->nb);
if (r)
return r;
if (omapfb_dev != NULL &&
omapfb_dev->ctrl && omapfb_dev->ctrl->bind_client) {
omapfb_dev->ctrl->bind_client(omapfb_nb);
}
return 0;
}
EXPORT_SYMBOL(omapfb_register_client);
int omapfb_unregister_client(struct omapfb_notifier_block *omapfb_nb)
{
return notifier_chain_unregister(&omapfb_client_list,
&omapfb_nb->nb);
}
EXPORT_SYMBOL(omapfb_unregister_client);
void omapfb_notify_clients(struct omapfb_device *fbdev, unsigned long event)
{
DBGENTER(1);
notifier_call_chain(&omapfb_client_list, event, fbdev);
}
EXPORT_SYMBOL(omapfb_notify_clients);
static int omapfb_set_update_mode(struct omapfb_device *fbdev,
enum omapfb_update_mode mode)
{
......@@ -1291,6 +1358,8 @@ static int omapfb_do_probe(struct platform_device *pdev, struct lcd_panel *panel
hhz = phz * 10 / (panel->hfp + panel->x_res + panel->hbp + panel->hsw);
vhz = hhz / (panel->vfp + panel->y_res + panel->vbp + panel->vsw);
omapfb_dev = fbdev;
pr_info(MODULE_NAME ": initialized vram=%lu "
"pixclock %lu kHz hfreq %lu.%lu kHz vfreq %lu.%lu Hz\n",
fbdev->vram_size,
......@@ -1306,9 +1375,6 @@ cleanup:
return r;
}
static struct platform_device *fbdev_pdev;
static struct lcd_panel *fbdev_panel;
static int omapfb_probe(struct platform_device *pdev)
{
BUG_ON(fbdev_pdev != NULL);
......
......@@ -182,6 +182,7 @@ struct omap_dsp_varinfo {
#define OMAP_DSP_MBCMD_KFUNC_FBCTL 0x00
#define OMAP_DSP_MBCMD_FBCTL_UPD 0x0000
#define OMAP_DSP_MBCMD_FBCTL_ENABLE 0x0002
#define OMAP_DSP_MBCMD_FBCTL_DISABLE 0x0003
......
......@@ -215,6 +215,15 @@ struct lcd_ctrl_extif {
unsigned long max_transmit_size;
};
struct omapfb_notifier_block {
struct notifier_block nb;
void *data;
};
typedef int (*omapfb_notifier_callback_t)(struct omapfb_notifier_block *,
unsigned long event,
struct omapfb_device *fbdev);
struct lcd_ctrl {
const char *name;
void *data;
......@@ -222,6 +231,7 @@ struct lcd_ctrl {
int (*init) (struct omapfb_device *fbdev,
int ext_mode, int req_vram_size);
void (*cleanup) (void);
void (*bind_client) (struct omapfb_notifier_block *nb);
void (*get_vram_layout)(unsigned long *size,
void **virt_base,
dma_addr_t *phys_base);
......@@ -285,6 +295,9 @@ struct omapfb_platform_data {
struct omap_fbmem_config fbmem;
};
#define OMAPFB_EVENT_READY 1
#define OMAPFB_EVENT_DISABLED 2
#ifdef CONFIG_ARCH_OMAP1
extern struct lcd_ctrl omap1_lcd_ctrl;
#else
......@@ -293,6 +306,15 @@ extern struct lcd_ctrl omap2_disp_ctrl;
extern void omapfb_register_panel(struct lcd_panel *panel);
extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval);
extern void omapfb_notify_clients(struct omapfb_device *fbdev,
unsigned long event);
extern int omapfb_register_client(struct omapfb_notifier_block *nb,
omapfb_notifier_callback_t callback,
void *callback_data);
extern int omapfb_unregister_client(struct omapfb_notifier_block *nb);
extern int omapfb_update_window_async(struct omapfb_update_window *win,
void (*callback)(void *),
void *callback_data);
/* in arch/arm/plat-omap/devices.c */
extern void omapfb_reserve_mem(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment