Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 120161
b: refs/heads/master
c: 8564557
h: refs/heads/master
i:
  120159: 2ee041b
v: v3
  • Loading branch information
Magnus Damm authored and Paul Mundt committed Dec 22, 2008
1 parent c6a32ca commit 4ec4991
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 23 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 37b4837959cb9aa60686ca0d85f73d819251abad
refs/heads/master: 8564557a03c12adb9c4b76ae1e86db4113a04d13
1 change: 1 addition & 0 deletions trunk/drivers/video/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1893,6 +1893,7 @@ config FB_SH_MOBILE_LCDC
select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
---help---
Frame buffer driver for the on-chip SH-Mobile LCD controller.

Expand Down
170 changes: 148 additions & 22 deletions trunk/drivers/video/sh_mobile_lcdcfb.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <video/sh_mobile_lcdc.h>
#include <asm/atomic.h>

#define PALETTE_NR 16

Expand All @@ -30,11 +32,14 @@ struct sh_mobile_lcdc_chan {
u32 pseudo_palette[PALETTE_NR];
struct fb_info info;
dma_addr_t dma_handle;
struct fb_deferred_io defio;
};

struct sh_mobile_lcdc_priv {
void __iomem *base;
int irq;
#ifdef CONFIG_HAVE_CLK
atomic_t clk_usecnt;
struct clk *dot_clk;
struct clk *clk;
#endif
Expand All @@ -57,7 +62,7 @@ struct sh_mobile_lcdc_priv {

/* per-channel registers */
enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };

static unsigned long lcdc_offs_mainlcd[] = {
[LDDCKPAT1R] = 0x400,
Expand All @@ -67,6 +72,7 @@ static unsigned long lcdc_offs_mainlcd[] = {
[LDMT3R] = 0x420,
[LDDFR] = 0x424,
[LDSM1R] = 0x428,
[LDSM2R] = 0x42c,
[LDSA1R] = 0x430,
[LDMLSR] = 0x438,
[LDHCNR] = 0x448,
Expand All @@ -84,6 +90,7 @@ static unsigned long lcdc_offs_sublcd[] = {
[LDMT3R] = 0x608,
[LDDFR] = 0x60c,
[LDSM1R] = 0x610,
[LDSM2R] = 0x614,
[LDSA1R] = 0x618,
[LDMLSR] = 0x620,
[LDHCNR] = 0x624,
Expand All @@ -97,6 +104,8 @@ static unsigned long lcdc_offs_sublcd[] = {
#define LCDC_RESET 0x00000100
#define DISPLAY_BEU 0x00000008
#define LCDC_ENABLE 0x00000001
#define LDINTR_FE 0x00000400
#define LDINTR_FS 0x00000004

static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
int reg_nr, unsigned long data)
Expand Down Expand Up @@ -171,6 +180,65 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
lcdc_sys_read_data,
};

#ifdef CONFIG_HAVE_CLK
static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
{
if (atomic_inc_and_test(&priv->clk_usecnt)) {
clk_enable(priv->clk);
if (priv->dot_clk)
clk_enable(priv->dot_clk);
}
}

static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
{
if (atomic_sub_return(1, &priv->clk_usecnt) == -1) {
if (priv->dot_clk)
clk_disable(priv->dot_clk);
clk_disable(priv->clk);
}
}
#else
static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) {}
static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {}
#endif

static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
struct list_head *pagelist)
{
struct sh_mobile_lcdc_chan *ch = info->par;

/* enable clocks before accessing hardware */
sh_mobile_lcdc_clk_on(ch->lcdc);

/* trigger panel update */
lcdc_write_chan(ch, LDSM2R, 1);
}

static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;

if (fbdefio)
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
}

static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
{
struct sh_mobile_lcdc_priv *priv = data;
unsigned long tmp;

/* acknowledge interrupt */
tmp = lcdc_read(priv, _LDINTR);
tmp &= 0xffffff00; /* mask in high 24 bits */
tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */
lcdc_write(priv, _LDINTR, tmp);

/* disable clocks */
sh_mobile_lcdc_clk_off(priv);
return IRQ_HANDLED;
}

static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
int start)
{
Expand Down Expand Up @@ -208,11 +276,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
int k, m;
int ret = 0;

#ifdef CONFIG_HAVE_CLK
clk_enable(priv->clk);
if (priv->dot_clk)
clk_enable(priv->dot_clk);
#endif
/* enable clocks before accessing the hardware */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
if (priv->ch[k].enabled)
sh_mobile_lcdc_clk_on(priv);

/* reset */
lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
Expand Down Expand Up @@ -255,7 +323,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
lcdc_write(priv, _LDDCKSTPR, 0);
lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);

/* interrupts are disabled */
/* interrupts are disabled to begin with */
lcdc_write(priv, _LDINTR, 0);

for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
Expand Down Expand Up @@ -316,9 +384,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
return ret;
}

/* --- display_lcdc_data() --- */
lcdc_write(priv, _LDINTR, 0x00000f00);

/* word and long word swap */
lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);

Expand All @@ -340,8 +405,24 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* set line size */
lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length);

/* continuous read mode */
lcdc_write_chan(ch, LDSM1R, 0);
/* setup deferred io if SYS bus */
tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
if (ch->ldmt1r_value & (1 << 12) && tmp) {
ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
ch->defio.delay = msecs_to_jiffies(tmp);
ch->info.fbdefio = &ch->defio;
fb_deferred_io_init(&ch->info);

/* one-shot mode */
lcdc_write_chan(ch, LDSM1R, 1);

/* enable "Frame End Interrupt Enable" bit */
lcdc_write(priv, _LDINTR, LDINTR_FE);

} else {
/* continuous read mode */
lcdc_write_chan(ch, LDSM1R, 0);
}
}

/* display output */
Expand All @@ -365,6 +446,7 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
{
struct sh_mobile_lcdc_chan *ch;
struct sh_mobile_lcdc_board_cfg *board_cfg;
unsigned long tmp;
int k;

/* tell the board code to disable the panel */
Expand All @@ -373,16 +455,22 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_off)
board_cfg->display_off(board_cfg->board_data);

/* cleanup deferred io if SYS bus */
tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
if (ch->ldmt1r_value & (1 << 12) && tmp) {
fb_deferred_io_cleanup(&ch->info);
ch->info.fbdefio = NULL;
}
}

/* stop the lcdc */
sh_mobile_lcdc_start_stop(priv, 0);

#ifdef CONFIG_HAVE_CLK
if (priv->dot_clk)
clk_disable(priv->dot_clk);
clk_disable(priv->clk);
#endif
/* stop clocks */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
if (priv->ch[k].enabled)
sh_mobile_lcdc_clk_off(priv);
}

static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
Expand Down Expand Up @@ -446,6 +534,7 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
priv->lddckr = icksel << 16;

#ifdef CONFIG_HAVE_CLK
atomic_set(&priv->clk_usecnt, -1);
snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id);
priv->clk = clk_get(&pdev->dev, clk_name);
if (IS_ERR(priv->clk)) {
Expand Down Expand Up @@ -497,13 +586,34 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
.accel = FB_ACCEL_NONE,
};

static void sh_mobile_lcdc_fillrect(struct fb_info *info,
const struct fb_fillrect *rect)
{
sys_fillrect(info, rect);
sh_mobile_lcdc_deferred_io_touch(info);
}

static void sh_mobile_lcdc_copyarea(struct fb_info *info,
const struct fb_copyarea *area)
{
sys_copyarea(info, area);
sh_mobile_lcdc_deferred_io_touch(info);
}

static void sh_mobile_lcdc_imageblit(struct fb_info *info,
const struct fb_image *image)
{
sys_imageblit(info, image);
sh_mobile_lcdc_deferred_io_touch(info);
}

static struct fb_ops sh_mobile_lcdc_ops = {
.fb_setcolreg = sh_mobile_lcdc_setcolreg,
.fb_read = fb_sys_read,
.fb_write = fb_sys_write,
.fb_fillrect = sys_fillrect,
.fb_copyarea = sys_copyarea,
.fb_imageblit = sys_imageblit,
.fb_fillrect = sh_mobile_lcdc_fillrect,
.fb_copyarea = sh_mobile_lcdc_copyarea,
.fb_imageblit = sh_mobile_lcdc_imageblit,
};

static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
Expand Down Expand Up @@ -564,8 +674,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
}

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "cannot find IO resource\n");
i = platform_get_irq(pdev, 0);
if (!res || i < 0) {
dev_err(&pdev->dev, "cannot get platform resources\n");
error = -ENOENT;
goto err0;
}
Expand All @@ -577,6 +688,14 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err0;
}

error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED,
pdev->dev.bus_id, priv);
if (error) {
dev_err(&pdev->dev, "unable to request irq\n");
goto err1;
}

priv->irq = i;
platform_set_drvdata(pdev, priv);
pdata = pdev->dev.platform_data;

Expand Down Expand Up @@ -660,6 +779,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
info->fix.smem_start = priv->ch[i].dma_handle;
info->screen_base = buf;
info->device = &pdev->dev;
info->par = &priv->ch[i];
}

if (error)
Expand Down Expand Up @@ -687,6 +807,10 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
(int) priv->ch[i].cfg.lcd_cfg.xres,
(int) priv->ch[i].cfg.lcd_cfg.yres,
priv->ch[i].cfg.bpp);

/* deferred io mode: disable clock to save power */
if (info->fbdefio)
sh_mobile_lcdc_clk_off(priv);
}

return 0;
Expand Down Expand Up @@ -728,6 +852,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
if (priv->base)
iounmap(priv->base);

if (priv->irq)
free_irq(priv->irq, priv);
kfree(priv);
return 0;
}
Expand Down
1 change: 1 addition & 0 deletions trunk/include/video/sh_mobile_lcdc.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ enum { LCDC_CLK_BUS, LCDC_CLK_PERIPHERAL, LCDC_CLK_EXTERNAL };
struct sh_mobile_lcdc_sys_bus_cfg {
unsigned long ldmt2r;
unsigned long ldmt3r;
unsigned long deferred_io_msec;
};

struct sh_mobile_lcdc_sys_bus_ops {
Expand Down

0 comments on commit 4ec4991

Please sign in to comment.