Commit 8edbeb6e authored by Florian Tobias Schandinat's avatar Florian Tobias Schandinat

Merge branch 'sh-mobile-lcdc' of git://linuxtv.org/pinchartl/fbdev into fbdev-next

parents d4a7dbfd 8a20974f
...@@ -1583,6 +1583,7 @@ static void __init mackerel_init(void) ...@@ -1583,6 +1583,7 @@ static void __init mackerel_init(void)
sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device); sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device); sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);
sh7372_add_device_to_domain(&sh7372_a4lc, &meram_device);
sh7372_add_device_to_domain(&sh7372_a4mp, &fsi_device); sh7372_add_device_to_domain(&sh7372_a4mp, &fsi_device);
hdmi_init_pm_clock(); hdmi_init_pm_clock();
......
...@@ -24,39 +24,14 @@ ...@@ -24,39 +24,14 @@
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <video/sh_mobile_lcdc.h> #include <video/sh_mobile_lcdc.h>
#include <video/sh_mobile_meram.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include "sh_mobile_lcdcfb.h" #include "sh_mobile_lcdcfb.h"
#include "sh_mobile_meram.h"
#define SIDE_B_OFFSET 0x1000 #define SIDE_B_OFFSET 0x1000
#define MIRROR_OFFSET 0x2000 #define MIRROR_OFFSET 0x2000
/* shared registers */
#define _LDDCKR 0x410
#define _LDDCKSTPR 0x414
#define _LDINTR 0x468
#define _LDSR 0x46c
#define _LDCNT1R 0x470
#define _LDCNT2R 0x474
#define _LDRCNTR 0x478
#define _LDDDSR 0x47c
#define _LDDWD0R 0x800
#define _LDDRDR 0x840
#define _LDDWAR 0x900
#define _LDDRAR 0x904
/* shared registers and their order for context save/restore */
static int lcdc_shared_regs[] = {
_LDDCKR,
_LDDCKSTPR,
_LDINTR,
_LDDDSR,
_LDCNT1R,
_LDCNT2R,
};
#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs)
#define MAX_XRES 1920 #define MAX_XRES 1920
#define MAX_YRES 1080 #define MAX_YRES 1080
...@@ -98,22 +73,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { ...@@ -98,22 +73,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
[LDPMR] = 0x63c, [LDPMR] = 0x63c,
}; };
#define START_LCDC 0x00000001
#define LCDC_RESET 0x00000100
#define DISPLAY_BEU 0x00000008
#define LCDC_ENABLE 0x00000001
#define LDINTR_FE 0x00000400
#define LDINTR_VSE 0x00000200
#define LDINTR_VEE 0x00000100
#define LDINTR_FS 0x00000004
#define LDINTR_VSS 0x00000002
#define LDINTR_VES 0x00000001
#define LDRCNTR_SRS 0x00020000
#define LDRCNTR_SRC 0x00010000
#define LDRCNTR_MRS 0x00000002
#define LDRCNTR_MRC 0x00000001
#define LDSR_MRS 0x00000100
static const struct fb_videomode default_720p = { static const struct fb_videomode default_720p = {
.name = "HDMI 720p", .name = "HDMI 720p",
.xres = 1280, .xres = 1280,
...@@ -141,7 +100,6 @@ struct sh_mobile_lcdc_priv { ...@@ -141,7 +100,6 @@ struct sh_mobile_lcdc_priv {
unsigned long lddckr; unsigned long lddckr;
struct sh_mobile_lcdc_chan ch[2]; struct sh_mobile_lcdc_chan ch[2];
struct notifier_block notifier; struct notifier_block notifier;
unsigned long saved_shared_regs[NR_SHARED_REGS];
int started; int started;
int forced_bpp; /* 2 channel LCDC must share bpp setting */ int forced_bpp; /* 2 channel LCDC must share bpp setting */
struct sh_mobile_meram_info *meram_dev; struct sh_mobile_meram_info *meram_dev;
...@@ -218,33 +176,36 @@ static void lcdc_sys_write_index(void *handle, unsigned long data) ...@@ -218,33 +176,36 @@ static void lcdc_sys_write_index(void *handle, unsigned long data)
{ {
struct sh_mobile_lcdc_chan *ch = handle; struct sh_mobile_lcdc_chan *ch = handle;
lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000); lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT);
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA |
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); (lcdc_chan_is_sublcd(ch) ? 2 : 0));
lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
} }
static void lcdc_sys_write_data(void *handle, unsigned long data) static void lcdc_sys_write_data(void *handle, unsigned long data)
{ {
struct sh_mobile_lcdc_chan *ch = handle; struct sh_mobile_lcdc_chan *ch = handle;
lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000); lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT | LDDWDxR_RSW);
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA |
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); (lcdc_chan_is_sublcd(ch) ? 2 : 0));
lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
} }
static unsigned long lcdc_sys_read_data(void *handle) static unsigned long lcdc_sys_read_data(void *handle)
{ {
struct sh_mobile_lcdc_chan *ch = handle; struct sh_mobile_lcdc_chan *ch = handle;
lcdc_write(ch->lcdc, _LDDRDR, 0x01000000); lcdc_write(ch->lcdc, _LDDRDR, LDDRDR_RSR);
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); lcdc_write(ch->lcdc, _LDDRAR, LDDRAR_RA |
(lcdc_chan_is_sublcd(ch) ? 2 : 0));
udelay(1); udelay(1);
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
return lcdc_read(ch->lcdc, _LDDRDR) & 0x3ffff; return lcdc_read(ch->lcdc, _LDDRDR) & LDDRDR_DRD_MASK;
} }
struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
...@@ -256,18 +217,22 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { ...@@ -256,18 +217,22 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
{ {
if (atomic_inc_and_test(&priv->hw_usecnt)) { if (atomic_inc_and_test(&priv->hw_usecnt)) {
pm_runtime_get_sync(priv->dev);
if (priv->dot_clk) if (priv->dot_clk)
clk_enable(priv->dot_clk); clk_enable(priv->dot_clk);
pm_runtime_get_sync(priv->dev);
if (priv->meram_dev && priv->meram_dev->pdev)
pm_runtime_get_sync(&priv->meram_dev->pdev->dev);
} }
} }
static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
{ {
if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
if (priv->meram_dev && priv->meram_dev->pdev)
pm_runtime_put_sync(&priv->meram_dev->pdev->dev);
pm_runtime_put(priv->dev);
if (priv->dot_clk) if (priv->dot_clk)
clk_disable(priv->dot_clk); clk_disable(priv->dot_clk);
pm_runtime_put(priv->dev);
} }
} }
...@@ -319,13 +284,13 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info, ...@@ -319,13 +284,13 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
if (bcfg->start_transfer) if (bcfg->start_transfer)
bcfg->start_transfer(bcfg->board_data, ch, bcfg->start_transfer(bcfg->board_data, ch,
&sh_mobile_lcdc_sys_bus_ops); &sh_mobile_lcdc_sys_bus_ops);
lcdc_write_chan(ch, LDSM2R, 1); lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG);
dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
} else { } else {
if (bcfg->start_transfer) if (bcfg->start_transfer)
bcfg->start_transfer(bcfg->board_data, ch, bcfg->start_transfer(bcfg->board_data, ch,
&sh_mobile_lcdc_sys_bus_ops); &sh_mobile_lcdc_sys_bus_ops);
lcdc_write_chan(ch, LDSM2R, 1); lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG);
} }
} }
...@@ -341,22 +306,16 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) ...@@ -341,22 +306,16 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
{ {
struct sh_mobile_lcdc_priv *priv = data; struct sh_mobile_lcdc_priv *priv = data;
struct sh_mobile_lcdc_chan *ch; struct sh_mobile_lcdc_chan *ch;
unsigned long tmp;
unsigned long ldintr; unsigned long ldintr;
int is_sub; int is_sub;
int k; int k;
/* acknowledge interrupt */ /* Acknowledge interrupts and disable further VSYNC End IRQs. */
ldintr = tmp = lcdc_read(priv, _LDINTR); ldintr = lcdc_read(priv, _LDINTR);
/* lcdc_write(priv, _LDINTR, (ldintr ^ LDINTR_STATUS_MASK) & ~LDINTR_VEE);
* disable further VSYNC End IRQs, preserve all other enabled IRQs,
* write 0 to bits 0-6 to ack all triggered IRQs.
*/
tmp &= 0xffffff00 & ~LDINTR_VEE;
lcdc_write(priv, _LDINTR, tmp);
/* figure out if this interrupt is for main or sub lcd */ /* figure out if this interrupt is for main or sub lcd */
is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0; is_sub = (lcdc_read(priv, _LDSR) & LDSR_MSS) ? 1 : 0;
/* wake up channel and disable clocks */ /* wake up channel and disable clocks */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
...@@ -365,7 +324,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) ...@@ -365,7 +324,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
if (!ch->enabled) if (!ch->enabled)
continue; continue;
/* Frame Start */ /* Frame End */
if (ldintr & LDINTR_FS) { if (ldintr & LDINTR_FS) {
if (is_sub == lcdc_chan_is_sublcd(ch)) { if (is_sub == lcdc_chan_is_sublcd(ch)) {
ch->frame_end = 1; ch->frame_end = 1;
...@@ -391,16 +350,17 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, ...@@ -391,16 +350,17 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
/* start or stop the lcdc */ /* start or stop the lcdc */
if (start) if (start)
lcdc_write(priv, _LDCNT2R, tmp | START_LCDC); lcdc_write(priv, _LDCNT2R, tmp | LDCNT2R_DO);
else else
lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC); lcdc_write(priv, _LDCNT2R, tmp & ~LDCNT2R_DO);
/* wait until power is applied/stopped on all channels */ /* wait until power is applied/stopped on all channels */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled) if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled)
while (1) { while (1) {
tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3; tmp = lcdc_read_chan(&priv->ch[k], LDPMR)
if (start && tmp == 3) & LDPMR_LPS;
if (start && tmp == LDPMR_LPS)
break; break;
if (!start && tmp == 0) if (!start && tmp == 0)
break; break;
...@@ -418,13 +378,13 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) ...@@ -418,13 +378,13 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
u32 tmp; u32 tmp;
tmp = ch->ldmt1r_value; tmp = ch->ldmt1r_value;
tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL;
tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL;
tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
lcdc_write_chan(ch, LDMT1R, tmp); lcdc_write_chan(ch, LDMT1R, tmp);
/* setup SYS bus */ /* setup SYS bus */
...@@ -463,242 +423,239 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) ...@@ -463,242 +423,239 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
lcdc_write_chan(ch, LDHAJR, tmp); lcdc_write_chan(ch, LDHAJR, tmp);
} }
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) /*
* __sh_mobile_lcdc_start - Configure and tart the LCDC
* @priv: LCDC device
*
* Configure all enabled channels and start the LCDC device. All external
* devices (clocks, MERAM, panels, ...) are not touched by this function.
*/
static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{ {
struct sh_mobile_lcdc_chan *ch; struct sh_mobile_lcdc_chan *ch;
struct sh_mobile_lcdc_board_cfg *board_cfg;
unsigned long tmp; unsigned long tmp;
int bpp = 0; int bpp = 0;
unsigned long ldddsr; int k, m;
int k, m, ret;
/* 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);
if (!bpp)
bpp = priv->ch[k].info->var.bits_per_pixel;
}
}
/* reset */
lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
/* enable LCDC channels */
tmp = lcdc_read(priv, _LDCNT2R);
tmp |= priv->ch[0].enabled;
tmp |= priv->ch[1].enabled;
lcdc_write(priv, _LDCNT2R, tmp);
/* read data from external memory, avoid using the BEU for now */ /* Enable LCDC channels. Read data from external memory, avoid using the
lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU); * BEU for now.
*/
lcdc_write(priv, _LDCNT2R, priv->ch[0].enabled | priv->ch[1].enabled);
/* stop the lcdc first */ /* Stop the LCDC first and disable all interrupts. */
sh_mobile_lcdc_start_stop(priv, 0); sh_mobile_lcdc_start_stop(priv, 0);
lcdc_write(priv, _LDINTR, 0);
/* configure clocks */ /* Configure power supply, dot clocks and start them. */
tmp = priv->lddckr; tmp = priv->lddckr;
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k]; ch = &priv->ch[k];
if (!ch->enabled)
if (!priv->ch[k].enabled)
continue; continue;
if (!bpp)
bpp = ch->info->var.bits_per_pixel;
/* Power supply */
lcdc_write_chan(ch, LDPMR, 0);
m = ch->cfg.clock_divider; m = ch->cfg.clock_divider;
if (!m) if (!m)
continue; continue;
if (m == 1) /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider
m = 1 << 6; * denominator.
tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); */
/* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */
lcdc_write_chan(ch, LDDCKPAT1R, 0); lcdc_write_chan(ch, LDDCKPAT1R, 0);
lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
if (m == 1)
m = LDDCKR_MOSEL;
tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
} }
lcdc_write(priv, _LDDCKR, tmp); lcdc_write(priv, _LDDCKR, tmp);
/* start dotclock again */
lcdc_write(priv, _LDDCKSTPR, 0); lcdc_write(priv, _LDDCKSTPR, 0);
lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
/* interrupts are disabled to begin with */ /* Setup geometry, format, frame buffer memory and operation mode. */
lcdc_write(priv, _LDINTR, 0);
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k]; ch = &priv->ch[k];
if (!ch->enabled) if (!ch->enabled)
continue; continue;
sh_mobile_lcdc_geometry(ch); sh_mobile_lcdc_geometry(ch);
/* power supply */ if (ch->info->var.nonstd) {
lcdc_write_chan(ch, LDPMR, 0); tmp = (ch->info->var.nonstd << 16);
switch (ch->info->var.bits_per_pixel) {
case 12:
tmp |= LDDFR_YF_420;
break;
case 16:
tmp |= LDDFR_YF_422;
break;
case 24:
default:
tmp |= LDDFR_YF_444;
break;
}
} else {
switch (ch->info->var.bits_per_pixel) {
case 16:
tmp = LDDFR_PKF_RGB16;
break;
case 24:
tmp = LDDFR_PKF_RGB24;
break;
case 32:
default:
tmp = LDDFR_PKF_ARGB32;
break;
}
}
board_cfg = &ch->cfg.board_cfg; lcdc_write_chan(ch, LDDFR, tmp);
if (board_cfg->setup_sys) { lcdc_write_chan(ch, LDMLSR, ch->pitch);
ret = board_cfg->setup_sys(board_cfg->board_data, lcdc_write_chan(ch, LDSA1R, ch->base_addr_y);
ch, &sh_mobile_lcdc_sys_bus_ops); if (ch->info->var.nonstd)
if (ret) lcdc_write_chan(ch, LDSA2R, ch->base_addr_c);
return ret;
/* When using deferred I/O mode, configure the LCDC for one-shot
* operation and enable the frame end interrupt. Otherwise use
* continuous read mode.
*/
if (ch->ldmt1r_value & LDMT1R_IFM &&
ch->cfg.sys_bus_cfg.deferred_io_msec) {
lcdc_write_chan(ch, LDSM1R, LDSM1R_OS);
lcdc_write(priv, _LDINTR, LDINTR_FE);
} else {
lcdc_write_chan(ch, LDSM1R, 0);
} }
} }
/* word and long word swap */ /* Word and long word swap. */
ldddsr = lcdc_read(priv, _LDDDSR);
if (priv->ch[0].info->var.nonstd) if (priv->ch[0].info->var.nonstd)
lcdc_write(priv, _LDDDSR, ldddsr | 7); tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
else { else {
switch (bpp) { switch (bpp) {
case 16: case 16:
lcdc_write(priv, _LDDDSR, ldddsr | 6); tmp = LDDDSR_LS | LDDDSR_WS;
break; break;
case 24: case 24:
lcdc_write(priv, _LDDDSR, ldddsr | 7); tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
break; break;
case 32: case 32:
lcdc_write(priv, _LDDDSR, ldddsr | 4); default:
tmp = LDDDSR_LS;
break; break;
} }
} }
lcdc_write(priv, _LDDDSR, tmp);
/* Enable the display output. */
lcdc_write(priv, _LDCNT1R, LDCNT1R_DE);
sh_mobile_lcdc_start_stop(priv, 1);
priv->started = 1;
}
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
struct sh_mobile_meram_info *mdev = priv->meram_dev;
struct sh_mobile_lcdc_board_cfg *board_cfg;
struct sh_mobile_lcdc_chan *ch;
unsigned long tmp;
int ret;
int k;
/* 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) | LDCNT2R_BR);
lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0);
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
unsigned long base_addr_y;
unsigned long base_addr_c = 0;
int pitch;
ch = &priv->ch[k]; ch = &priv->ch[k];
if (!priv->ch[k].enabled) if (!ch->enabled)
continue; continue;
/* set bpp format in PKF[4:0] */ board_cfg = &ch->cfg.board_cfg;
tmp = lcdc_read_chan(ch, LDDFR); if (board_cfg->setup_sys) {
tmp &= ~0x0003031f; ret = board_cfg->setup_sys(board_cfg->board_data, ch,
if (ch->info->var.nonstd) { &sh_mobile_lcdc_sys_bus_ops);
tmp |= (ch->info->var.nonstd << 16); if (ret)
switch (ch->info->var.bits_per_pixel) { return ret;
case 12:
break;
case 16:
tmp |= (0x1 << 8);
break;
case 24:
tmp |= (0x2 << 8);
break;
}
} else {
switch (ch->info->var.bits_per_pixel) {
case 16:
tmp |= 0x03;
break;
case 24:
tmp |= 0x0b;
break;
case 32:
break;
} }
} }
lcdc_write_chan(ch, LDDFR, tmp);
base_addr_y = ch->info->fix.smem_start;
base_addr_c = base_addr_y +
ch->info->var.xres *
ch->info->var.yres_virtual;
pitch = ch->info->fix.line_length;
/* test if we can enable meram */ /* Compute frame buffer base address and pitch for each channel. */
if (ch->cfg.meram_cfg && priv->meram_dev && for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
priv->meram_dev->ops) {
struct sh_mobile_meram_cfg *cfg; struct sh_mobile_meram_cfg *cfg;
struct sh_mobile_meram_info *mdev; int pixelformat;
unsigned long icb_addr_y, icb_addr_c;
int icb_pitch;
int pf;
ch = &priv->ch[k];
if (!ch->enabled)
continue;
ch->base_addr_y = ch->info->fix.smem_start;
ch->base_addr_c = ch->base_addr_y
+ ch->info->var.xres
* ch->info->var.yres_virtual;
ch->pitch = ch->info->fix.line_length;
/* Enable MERAM if possible. */
cfg = ch->cfg.meram_cfg; cfg = ch->cfg.meram_cfg;
mdev = priv->meram_dev; if (mdev == NULL || mdev->ops == NULL || cfg == NULL)
/* we need to de-init configured ICBs before we continue;
* we can re-initialize them.
/* we need to de-init configured ICBs before we can
* re-initialize them.
*/ */
if (ch->meram_enabled) if (ch->meram_enabled) {
mdev->ops->meram_unregister(mdev, cfg); mdev->ops->meram_unregister(mdev, cfg);
ch->meram_enabled = 0; ch->meram_enabled = 0;
}
if (ch->info->var.nonstd) { if (!ch->info->var.nonstd)
if (ch->info->var.bits_per_pixel == 24) pixelformat = SH_MOBILE_MERAM_PF_RGB;
pf = SH_MOBILE_MERAM_PF_NV24; else if (ch->info->var.bits_per_pixel == 24)
pixelformat = SH_MOBILE_MERAM_PF_NV24;
else else
pf = SH_MOBILE_MERAM_PF_NV; pixelformat = SH_MOBILE_MERAM_PF_NV;
} else {
pf = SH_MOBILE_MERAM_PF_RGB;
}
ret = mdev->ops->meram_register(mdev, cfg, pitch,
ch->info->var.yres,
pf,
base_addr_y,
base_addr_c,
&icb_addr_y,
&icb_addr_c,
&icb_pitch);
if (!ret) {
/* set LDSA1R value */
base_addr_y = icb_addr_y;
pitch = icb_pitch;
/* set LDSA2R value if required */
if (base_addr_c)
base_addr_c = icb_addr_c;
ret = mdev->ops->meram_register(mdev, cfg, ch->pitch,
ch->info->var.yres, pixelformat,
ch->base_addr_y, ch->base_addr_c,
&ch->base_addr_y, &ch->base_addr_c,
&ch->pitch);
if (!ret)
ch->meram_enabled = 1; ch->meram_enabled = 1;
} }
}
/* point out our frame buffer */ /* Start the LCDC. */
lcdc_write_chan(ch, LDSA1R, base_addr_y); __sh_mobile_lcdc_start(priv);
if (ch->info->var.nonstd)
lcdc_write_chan(ch, LDSA2R, base_addr_c);
/* set line size */ /* Setup deferred I/O, tell the board code to enable the panels, and
lcdc_write_chan(ch, LDMLSR, pitch); * turn backlight on.
*/
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k];
if (!ch->enabled)
continue;
/* setup deferred io if SYS bus */
tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
if (ch->ldmt1r_value & (1 << 12) && tmp) { if (ch->ldmt1r_value & LDMT1R_IFM && tmp) {
ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
ch->defio.delay = msecs_to_jiffies(tmp); ch->defio.delay = msecs_to_jiffies(tmp);
ch->info->fbdefio = &ch->defio; ch->info->fbdefio = &ch->defio;
fb_deferred_io_init(ch->info); 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 */
lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);
/* start the lcdc */
sh_mobile_lcdc_start_stop(priv, 1);
priv->started = 1;
/* tell the board code to enable the panel */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k];
if (!ch->enabled)
continue;
board_cfg = &ch->cfg.board_cfg; board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_on && try_module_get(board_cfg->owner)) { if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
board_cfg->display_on(board_cfg->board_data, ch->info); board_cfg->display_on(board_cfg->board_data, ch->info);
...@@ -776,42 +733,42 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) ...@@ -776,42 +733,42 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
{ {
int ifm, miftyp; int interface_type = ch->cfg.interface_type;
switch (ch->cfg.interface_type) { switch (interface_type) {
case RGB8: ifm = 0; miftyp = 0; break; case RGB8:
case RGB9: ifm = 0; miftyp = 4; break; case RGB9:
case RGB12A: ifm = 0; miftyp = 5; break; case RGB12A:
case RGB12B: ifm = 0; miftyp = 6; break; case RGB12B:
case RGB16: ifm = 0; miftyp = 7; break; case RGB16:
case RGB18: ifm = 0; miftyp = 10; break; case RGB18:
case RGB24: ifm = 0; miftyp = 11; break; case RGB24:
case SYS8A: ifm = 1; miftyp = 0; break; case SYS8A:
case SYS8B: ifm = 1; miftyp = 1; break; case SYS8B:
case SYS8C: ifm = 1; miftyp = 2; break; case SYS8C:
case SYS8D: ifm = 1; miftyp = 3; break; case SYS8D:
case SYS9: ifm = 1; miftyp = 4; break; case SYS9:
case SYS12: ifm = 1; miftyp = 5; break; case SYS12:
case SYS16A: ifm = 1; miftyp = 7; break; case SYS16A:
case SYS16B: ifm = 1; miftyp = 8; break; case SYS16B:
case SYS16C: ifm = 1; miftyp = 9; break; case SYS16C:
case SYS18: ifm = 1; miftyp = 10; break; case SYS18:
case SYS24: ifm = 1; miftyp = 11; break; case SYS24:
default: goto bad; break;
default:
return -EINVAL;
} }
/* SUBLCD only supports SYS interface */ /* SUBLCD only supports SYS interface */
if (lcdc_chan_is_sublcd(ch)) { if (lcdc_chan_is_sublcd(ch)) {
if (ifm == 0) if (!(interface_type & LDMT1R_IFM))
goto bad; return -EINVAL;
else
ifm = 0; interface_type &= ~LDMT1R_IFM;
} }
ch->ldmt1r_value = (ifm << 12) | miftyp; ch->ldmt1r_value = interface_type;
return 0; return 0;
bad:
return -EINVAL;
} }
static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
...@@ -819,18 +776,24 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, ...@@ -819,18 +776,24 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
struct sh_mobile_lcdc_priv *priv) struct sh_mobile_lcdc_priv *priv)
{ {
char *str; char *str;
int icksel;
switch (clock_source) { switch (clock_source) {
case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break; case LCDC_CLK_BUS:
case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break; str = "bus_clk";
case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break; priv->lddckr = LDDCKR_ICKSEL_BUS;
break;
case LCDC_CLK_PERIPHERAL:
str = "peripheral_clk";
priv->lddckr = LDDCKR_ICKSEL_MIPI;
break;
case LCDC_CLK_EXTERNAL:
str = NULL;
priv->lddckr = LDDCKR_ICKSEL_HDMI;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
priv->lddckr = icksel << 16;
if (str) { if (str) {
priv->dot_clk = clk_get(&pdev->dev, str); priv->dot_clk = clk_get(&pdev->dev, str);
if (IS_ERR(priv->dot_clk)) { if (IS_ERR(priv->dot_clk)) {
...@@ -940,32 +903,28 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, ...@@ -940,32 +903,28 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
base_addr_c += 2 * var->xoffset; base_addr_c += 2 * var->xoffset;
else else
base_addr_c += var->xoffset; base_addr_c += var->xoffset;
} else }
base_addr_c = 0;
if (!ch->meram_enabled) { if (ch->meram_enabled) {
lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
if (base_addr_c)
lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
} else {
struct sh_mobile_meram_cfg *cfg; struct sh_mobile_meram_cfg *cfg;
struct sh_mobile_meram_info *mdev; struct sh_mobile_meram_info *mdev;
unsigned long icb_addr_y, icb_addr_c;
int ret; int ret;
cfg = ch->cfg.meram_cfg; cfg = ch->cfg.meram_cfg;
mdev = priv->meram_dev; mdev = priv->meram_dev;
ret = mdev->ops->meram_update(mdev, cfg, ret = mdev->ops->meram_update(mdev, cfg,
base_addr_y, base_addr_c, base_addr_y, base_addr_c,
&icb_addr_y, &icb_addr_c); &base_addr_y, &base_addr_c);
if (ret) if (ret)
return ret; return ret;
}
lcdc_write_chan_mirror(ch, LDSA1R, icb_addr_y); ch->base_addr_y = base_addr_y;
if (icb_addr_c) ch->base_addr_c = base_addr_c;
lcdc_write_chan_mirror(ch, LDSA2R, icb_addr_c);
} lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
if (var->nonstd)
lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
if (lcdc_chan_is_sublcd(ch)) if (lcdc_chan_is_sublcd(ch))
lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS); lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
...@@ -985,9 +944,11 @@ static int sh_mobile_wait_for_vsync(struct fb_info *info) ...@@ -985,9 +944,11 @@ static int sh_mobile_wait_for_vsync(struct fb_info *info)
unsigned long ldintr; unsigned long ldintr;
int ret; int ret;
/* Enable VSync End interrupt */ /* Enable VSync End interrupt and be careful not to acknowledge any
* pending interrupt.
*/
ldintr = lcdc_read(ch->lcdc, _LDINTR); ldintr = lcdc_read(ch->lcdc, _LDINTR);
ldintr |= LDINTR_VEE; ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK;
lcdc_write(ch->lcdc, _LDINTR, ldintr); lcdc_write(ch->lcdc, _LDINTR, ldintr);
ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion, ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
...@@ -1316,47 +1277,20 @@ static int sh_mobile_lcdc_resume(struct device *dev) ...@@ -1316,47 +1277,20 @@ static int sh_mobile_lcdc_resume(struct device *dev)
static int sh_mobile_lcdc_runtime_suspend(struct device *dev) static int sh_mobile_lcdc_runtime_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
struct sh_mobile_lcdc_chan *ch;
int k, n;
/* save per-channel registers */
for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
ch = &p->ch[k];
if (!ch->enabled)
continue;
for (n = 0; n < NR_CH_REGS; n++)
ch->saved_ch_regs[n] = lcdc_read_chan(ch, n);
}
/* save shared registers */
for (n = 0; n < NR_SHARED_REGS; n++)
p->saved_shared_regs[n] = lcdc_read(p, lcdc_shared_regs[n]);
/* turn off LCDC hardware */ /* turn off LCDC hardware */
lcdc_write(p, _LDCNT1R, 0); lcdc_write(priv, _LDCNT1R, 0);
return 0; return 0;
} }
static int sh_mobile_lcdc_runtime_resume(struct device *dev) static int sh_mobile_lcdc_runtime_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
struct sh_mobile_lcdc_chan *ch;
int k, n;
/* restore per-channel registers */
for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
ch = &p->ch[k];
if (!ch->enabled)
continue;
for (n = 0; n < NR_CH_REGS; n++)
lcdc_write_chan(ch, n, ch->saved_ch_regs[n]);
}
/* restore shared registers */ __sh_mobile_lcdc_start(priv);
for (n = 0; n < NR_SHARED_REGS; n++)
lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]);
return 0; return 0;
} }
...@@ -1472,12 +1406,12 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) ...@@ -1472,12 +1406,12 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
switch (pdata->ch[i].chan) { switch (pdata->ch[i].chan) {
case LCDC_CHAN_MAINLCD: case LCDC_CHAN_MAINLCD:
ch->enabled = 1 << 1; ch->enabled = LDCNT2R_ME;
ch->reg_offs = lcdc_offs_mainlcd; ch->reg_offs = lcdc_offs_mainlcd;
j++; j++;
break; break;
case LCDC_CHAN_SUBLCD: case LCDC_CHAN_SUBLCD:
ch->enabled = 1 << 2; ch->enabled = LDCNT2R_SE;
ch->reg_offs = lcdc_offs_sublcd; ch->reg_offs = lcdc_offs_sublcd;
j++; j++;
break; break;
......
...@@ -18,6 +18,13 @@ struct sh_mobile_lcdc_priv; ...@@ -18,6 +18,13 @@ struct sh_mobile_lcdc_priv;
struct fb_info; struct fb_info;
struct backlight_device; struct backlight_device;
/*
* struct sh_mobile_lcdc_chan - LCDC display channel
*
* @base_addr_y: Frame buffer viewport base address (luma component)
* @base_addr_c: Frame buffer viewport base address (chroma component)
* @pitch: Frame buffer line pitch
*/
struct sh_mobile_lcdc_chan { struct sh_mobile_lcdc_chan {
struct sh_mobile_lcdc_priv *lcdc; struct sh_mobile_lcdc_priv *lcdc;
unsigned long *reg_offs; unsigned long *reg_offs;
...@@ -25,7 +32,6 @@ struct sh_mobile_lcdc_chan { ...@@ -25,7 +32,6 @@ struct sh_mobile_lcdc_chan {
unsigned long enabled; /* ME and SE in LDCNT2R */ unsigned long enabled; /* ME and SE in LDCNT2R */
struct sh_mobile_lcdc_chan_cfg cfg; struct sh_mobile_lcdc_chan_cfg cfg;
u32 pseudo_palette[PALETTE_NR]; u32 pseudo_palette[PALETTE_NR];
unsigned long saved_ch_regs[NR_CH_REGS];
struct fb_info *info; struct fb_info *info;
struct backlight_device *bl; struct backlight_device *bl;
dma_addr_t dma_handle; dma_addr_t dma_handle;
...@@ -40,6 +46,10 @@ struct sh_mobile_lcdc_chan { ...@@ -40,6 +46,10 @@ struct sh_mobile_lcdc_chan {
int blank_status; int blank_status;
struct mutex open_lock; /* protects the use counter */ struct mutex open_lock; /* protects the use counter */
int meram_enabled; int meram_enabled;
unsigned long base_addr_y;
unsigned long base_addr_c;
unsigned int pitch;
}; };
#endif #endif
...@@ -12,30 +12,104 @@ ...@@ -12,30 +12,104 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/pm_runtime.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <video/sh_mobile_meram.h>
#include "sh_mobile_meram.h"
/* meram registers */ /* meram registers */
#define MExxCTL 0x0
#define MExxBSIZE 0x4
#define MExxMNCF 0x8
#define MExxSARA 0x10
#define MExxSARB 0x14
#define MExxSBSIZE 0x18
#define MERAM_MExxCTL_VAL(ctl, next_icb, addr) \
((ctl) | (((next_icb) & 0x1f) << 11) | (((addr) & 0x7ff) << 16))
#define MERAM_MExxBSIZE_VAL(a, b, c) \
(((a) << 28) | ((b) << 16) | (c))
#define MEVCR1 0x4 #define MEVCR1 0x4
#define MEACTS 0x10 #define MEVCR1_RST (1 << 31)
#define MEVCR1_WD (1 << 30)
#define MEVCR1_AMD1 (1 << 29)
#define MEVCR1_AMD0 (1 << 28)
#define MEQSEL1 0x40 #define MEQSEL1 0x40
#define MEQSEL2 0x44 #define MEQSEL2 0x44
#define MExxCTL 0x400
#define MExxCTL_BV (1 << 31)
#define MExxCTL_BSZ_SHIFT 28
#define MExxCTL_MSAR_MASK (0x7ff << MExxCTL_MSAR_SHIFT)
#define MExxCTL_MSAR_SHIFT 16
#define MExxCTL_NXT_MASK (0x1f << MExxCTL_NXT_SHIFT)
#define MExxCTL_NXT_SHIFT 11
#define MExxCTL_WD1 (1 << 10)
#define MExxCTL_WD0 (1 << 9)
#define MExxCTL_WS (1 << 8)
#define MExxCTL_CB (1 << 7)
#define MExxCTL_WBF (1 << 6)
#define MExxCTL_WF (1 << 5)
#define MExxCTL_RF (1 << 4)
#define MExxCTL_CM (1 << 3)
#define MExxCTL_MD_READ (1 << 0)
#define MExxCTL_MD_WRITE (2 << 0)
#define MExxCTL_MD_ICB_WB (3 << 0)
#define MExxCTL_MD_ICB (4 << 0)
#define MExxCTL_MD_FB (7 << 0)
#define MExxCTL_MD_MASK (7 << 0)
#define MExxBSIZE 0x404
#define MExxBSIZE_RCNT_SHIFT 28
#define MExxBSIZE_YSZM1_SHIFT 16
#define MExxBSIZE_XSZM1_SHIFT 0
#define MExxMNCF 0x408
#define MExxMNCF_KWBNM_SHIFT 28
#define MExxMNCF_KRBNM_SHIFT 24
#define MExxMNCF_BNM_SHIFT 16
#define MExxMNCF_XBV (1 << 15)
#define MExxMNCF_CPL_YCBCR444 (1 << 12)
#define MExxMNCF_CPL_YCBCR420 (2 << 12)
#define MExxMNCF_CPL_YCBCR422 (3 << 12)
#define MExxMNCF_CPL_MSK (3 << 12)
#define MExxMNCF_BL (1 << 2)
#define MExxMNCF_LNM_SHIFT 0
#define MExxSARA 0x410
#define MExxSARB 0x414
#define MExxSBSIZE 0x418
#define MExxSBSIZE_HDV (1 << 31)
#define MExxSBSIZE_HSZ16 (0 << 28)
#define MExxSBSIZE_HSZ32 (1 << 28)
#define MExxSBSIZE_HSZ64 (2 << 28)
#define MExxSBSIZE_HSZ128 (3 << 28)
#define MExxSBSIZE_SBSIZZ_SHIFT 0
#define MERAM_MExxCTL_VAL(next, addr) \
((((next) << MExxCTL_NXT_SHIFT) & MExxCTL_NXT_MASK) | \
(((addr) << MExxCTL_MSAR_SHIFT) & MExxCTL_MSAR_MASK))
#define MERAM_MExxBSIZE_VAL(rcnt, yszm1, xszm1) \
(((rcnt) << MExxBSIZE_RCNT_SHIFT) | \
((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
((xszm1) << MExxBSIZE_XSZM1_SHIFT))
#define SH_MOBILE_MERAM_ICB_NUM 32
static unsigned long common_regs[] = {
MEVCR1,
MEQSEL1,
MEQSEL2,
};
#define CMN_REGS_SIZE ARRAY_SIZE(common_regs)
static unsigned long icb_regs[] = {
MExxCTL,
MExxBSIZE,
MExxMNCF,
MExxSARA,
MExxSARB,
MExxSBSIZE,
};
#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
struct sh_mobile_meram_priv {
void __iomem *base;
struct mutex lock;
unsigned long used_icb;
int used_meram_cache_regions;
unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM];
unsigned long cmn_saved_regs[CMN_REGS_SIZE];
unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM];
};
/* settings */ /* settings */
#define MERAM_SEC_LINE 15 #define MERAM_SEC_LINE 15
#define MERAM_LINE_WIDTH 2048 #define MERAM_LINE_WIDTH 2048
...@@ -44,8 +118,7 @@ ...@@ -44,8 +118,7 @@
* MERAM/ICB access functions * MERAM/ICB access functions
*/ */
#define MERAM_ICB_OFFSET(base, idx, off) \ #define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
((base) + (0x400 + ((idx) * 0x20) + (off)))
static inline void meram_write_icb(void __iomem *base, int idx, int off, static inline void meram_write_icb(void __iomem *base, int idx, int off,
unsigned long val) unsigned long val)
...@@ -280,17 +353,18 @@ static int meram_init(struct sh_mobile_meram_priv *priv, ...@@ -280,17 +353,18 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
/* /*
* Set MERAM for framebuffer * Set MERAM for framebuffer
* *
* 0x70f: WD = 0x3, WS=0x1, CM=0x1, MD=FB mode
* we also chain the cache_icb and the marker_icb. * we also chain the cache_icb and the marker_icb.
* we also split the allocated MERAM buffer between two ICBs. * we also split the allocated MERAM buffer between two ICBs.
*/ */
meram_write_icb(priv->base, icb->cache_icb, MExxCTL, meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
MERAM_MExxCTL_VAL(0x70f, icb->marker_icb, MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) |
icb->meram_offset)); MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
MExxCTL_MD_FB);
meram_write_icb(priv->base, icb->marker_icb, MExxCTL, meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
MERAM_MExxCTL_VAL(0x70f, icb->cache_icb, MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset +
icb->meram_offset + icb->meram_size / 2) |
icb->meram_size / 2)); MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
MExxCTL_MD_FB);
return 0; return 0;
} }
...@@ -337,24 +411,22 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, ...@@ -337,24 +411,22 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
xres, yres, (!pixelformat) ? "yuv" : "rgb", xres, yres, (!pixelformat) ? "yuv" : "rgb",
base_addr_y, base_addr_c); base_addr_y, base_addr_c);
mutex_lock(&priv->lock);
/* we can't handle wider than 8192px */ /* we can't handle wider than 8192px */
if (xres > 8192) { if (xres > 8192) {
dev_err(&pdev->dev, "width exceeding the limit (> 8192)."); dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
error = -EINVAL; return -EINVAL;
goto err;
}
if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) {
dev_err(&pdev->dev, "no more ICB available.");
error = -EINVAL;
goto err;
} }
/* do we have at least one ICB config? */ /* do we have at least one ICB config? */
if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) { if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
dev_err(&pdev->dev, "at least one ICB is required."); dev_err(&pdev->dev, "at least one ICB is required.");
return -EINVAL;
}
mutex_lock(&priv->lock);
if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) {
dev_err(&pdev->dev, "no more ICB available.");
error = -EINVAL; error = -EINVAL;
goto err; goto err;
} }
...@@ -460,6 +532,57 @@ static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, ...@@ -460,6 +532,57 @@ static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
return 0; return 0;
} }
static int sh_mobile_meram_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
int k, j;
for (k = 0; k < CMN_REGS_SIZE; k++)
priv->cmn_saved_regs[k] = meram_read_reg(priv->base,
common_regs[k]);
for (j = 0; j < 32; j++) {
if (!test_bit(j, &priv->used_icb))
continue;
for (k = 0; k < ICB_REGS_SIZE; k++) {
priv->icb_saved_regs[j * ICB_REGS_SIZE + k] =
meram_read_icb(priv->base, j, icb_regs[k]);
/* Reset ICB on resume */
if (icb_regs[k] == MExxCTL)
priv->icb_saved_regs[j * ICB_REGS_SIZE + k] |=
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
}
}
return 0;
}
static int sh_mobile_meram_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
int k, j;
for (j = 0; j < 32; j++) {
if (!test_bit(j, &priv->used_icb))
continue;
for (k = 0; k < ICB_REGS_SIZE; k++) {
meram_write_icb(priv->base, j, icb_regs[k],
priv->icb_saved_regs[j * ICB_REGS_SIZE + k]);
}
}
for (k = 0; k < CMN_REGS_SIZE; k++)
meram_write_reg(priv->base, common_regs[k],
priv->cmn_saved_regs[k]);
return 0;
}
static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = {
.runtime_suspend = sh_mobile_meram_runtime_suspend,
.runtime_resume = sh_mobile_meram_runtime_resume,
};
static struct sh_mobile_meram_ops sh_mobile_meram_ops = { static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
.module = THIS_MODULE, .module = THIS_MODULE,
.meram_register = sh_mobile_meram_register, .meram_register = sh_mobile_meram_register,
...@@ -513,7 +636,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) ...@@ -513,7 +636,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
/* initialize ICB addressing mode */ /* initialize ICB addressing mode */
if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1) if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
meram_write_reg(priv->base, MEVCR1, 1 << 29); meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
pm_runtime_enable(&pdev->dev);
dev_info(&pdev->dev, "sh_mobile_meram initialized."); dev_info(&pdev->dev, "sh_mobile_meram initialized.");
...@@ -530,6 +655,8 @@ static int sh_mobile_meram_remove(struct platform_device *pdev) ...@@ -530,6 +655,8 @@ static int sh_mobile_meram_remove(struct platform_device *pdev)
{ {
struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
if (priv->base) if (priv->base)
iounmap(priv->base); iounmap(priv->base);
...@@ -544,6 +671,7 @@ static struct platform_driver sh_mobile_meram_driver = { ...@@ -544,6 +671,7 @@ static struct platform_driver sh_mobile_meram_driver = {
.driver = { .driver = {
.name = "sh_mobile_meram", .name = "sh_mobile_meram",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &sh_mobile_meram_dev_pm_ops,
}, },
.probe = sh_mobile_meram_probe, .probe = sh_mobile_meram_probe,
.remove = sh_mobile_meram_remove, .remove = sh_mobile_meram_remove,
......
#ifndef __sh_mobile_meram_h__
#define __sh_mobile_meram_h__
#include <linux/mutex.h>
#include <video/sh_mobile_meram.h>
/*
* MERAM private
*/
#define MERAM_ICB_Y 0x1
#define MERAM_ICB_C 0x2
/* MERAM cache size */
#define SH_MOBILE_MERAM_ICB_NUM 32
#define SH_MOBILE_MERAM_CACHE_OFFSET(p) ((p) >> 16)
#define SH_MOBILE_MERAM_CACHE_SIZE(p) ((p) & 0xffff)
struct sh_mobile_meram_priv {
void __iomem *base;
struct mutex lock;
unsigned long used_icb;
int used_meram_cache_regions;
unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM];
};
int sh_mobile_meram_alloc_icb(const struct sh_mobile_meram_cfg *cfg,
int xres,
int yres,
unsigned int base_addr,
int yuv_mode,
int *marker_icb,
int *out_pitch);
void sh_mobile_meram_free_icb(int marker_icb);
#define SH_MOBILE_MERAM_START(ind, ab) \
(0xC0000000 | ((ab & 0x1) << 23) | ((ind & 0x1F) << 24))
#endif /* !__sh_mobile_meram_h__ */
...@@ -4,26 +4,123 @@ ...@@ -4,26 +4,123 @@
#include <linux/fb.h> #include <linux/fb.h>
#include <video/sh_mobile_meram.h> #include <video/sh_mobile_meram.h>
/* Register definitions */
#define _LDDCKR 0x410
#define LDDCKR_ICKSEL_BUS (0 << 16)
#define LDDCKR_ICKSEL_MIPI (1 << 16)
#define LDDCKR_ICKSEL_HDMI (2 << 16)
#define LDDCKR_ICKSEL_EXT (3 << 16)
#define LDDCKR_ICKSEL_MASK (7 << 16)
#define LDDCKR_MOSEL (1 << 6)
#define _LDDCKSTPR 0x414
#define _LDINTR 0x468
#define LDINTR_FE (1 << 10)
#define LDINTR_VSE (1 << 9)
#define LDINTR_VEE (1 << 8)
#define LDINTR_FS (1 << 2)
#define LDINTR_VSS (1 << 1)
#define LDINTR_VES (1 << 0)
#define LDINTR_STATUS_MASK (0xff << 0)
#define _LDSR 0x46c
#define LDSR_MSS (1 << 10)
#define LDSR_MRS (1 << 8)
#define LDSR_AS (1 << 1)
#define _LDCNT1R 0x470
#define LDCNT1R_DE (1 << 0)
#define _LDCNT2R 0x474
#define LDCNT2R_BR (1 << 8)
#define LDCNT2R_MD (1 << 3)
#define LDCNT2R_SE (1 << 2)
#define LDCNT2R_ME (1 << 1)
#define LDCNT2R_DO (1 << 0)
#define _LDRCNTR 0x478
#define LDRCNTR_SRS (1 << 17)
#define LDRCNTR_SRC (1 << 16)
#define LDRCNTR_MRS (1 << 1)
#define LDRCNTR_MRC (1 << 0)
#define _LDDDSR 0x47c
#define LDDDSR_LS (1 << 2)
#define LDDDSR_WS (1 << 1)
#define LDDDSR_BS (1 << 0)
#define LDMT1R_VPOL (1 << 28)
#define LDMT1R_HPOL (1 << 27)
#define LDMT1R_DWPOL (1 << 26)
#define LDMT1R_DIPOL (1 << 25)
#define LDMT1R_DAPOL (1 << 24)
#define LDMT1R_HSCNT (1 << 17)
#define LDMT1R_DWCNT (1 << 16)
#define LDMT1R_IFM (1 << 12)
#define LDMT1R_MIFTYP_RGB8 (0x0 << 0)
#define LDMT1R_MIFTYP_RGB9 (0x4 << 0)
#define LDMT1R_MIFTYP_RGB12A (0x5 << 0)
#define LDMT1R_MIFTYP_RGB12B (0x6 << 0)
#define LDMT1R_MIFTYP_RGB16 (0x7 << 0)
#define LDMT1R_MIFTYP_RGB18 (0xa << 0)
#define LDMT1R_MIFTYP_RGB24 (0xb << 0)
#define LDMT1R_MIFTYP_YCBCR (0xf << 0)
#define LDMT1R_MIFTYP_SYS8A (0x0 << 0)
#define LDMT1R_MIFTYP_SYS8B (0x1 << 0)
#define LDMT1R_MIFTYP_SYS8C (0x2 << 0)
#define LDMT1R_MIFTYP_SYS8D (0x3 << 0)
#define LDMT1R_MIFTYP_SYS9 (0x4 << 0)
#define LDMT1R_MIFTYP_SYS12 (0x5 << 0)
#define LDMT1R_MIFTYP_SYS16A (0x7 << 0)
#define LDMT1R_MIFTYP_SYS16B (0x8 << 0)
#define LDMT1R_MIFTYP_SYS16C (0x9 << 0)
#define LDMT1R_MIFTYP_SYS18 (0xa << 0)
#define LDMT1R_MIFTYP_SYS24 (0xb << 0)
#define LDMT1R_MIFTYP_MASK (0xf << 0)
#define LDDFR_CF1 (1 << 18)
#define LDDFR_CF0 (1 << 17)
#define LDDFR_CC (1 << 16)
#define LDDFR_YF_420 (0 << 8)
#define LDDFR_YF_422 (1 << 8)
#define LDDFR_YF_444 (2 << 8)
#define LDDFR_YF_MASK (3 << 8)
#define LDDFR_PKF_ARGB32 (0x00 << 0)
#define LDDFR_PKF_RGB16 (0x03 << 0)
#define LDDFR_PKF_RGB24 (0x0b << 0)
#define LDDFR_PKF_MASK (0x1f << 0)
#define LDSM1R_OS (1 << 0)
#define LDSM2R_OSTRG (1 << 0)
#define LDPMR_LPS (3 << 0)
#define _LDDWD0R 0x800
#define LDDWDxR_WDACT (1 << 28)
#define LDDWDxR_RSW (1 << 24)
#define _LDDRDR 0x840
#define LDDRDR_RSR (1 << 24)
#define LDDRDR_DRD_MASK (0x3ffff << 0)
#define _LDDWAR 0x900
#define LDDWAR_WA (1 << 0)
#define _LDDRAR 0x904
#define LDDRAR_RA (1 << 0)
enum { enum {
RGB8, /* 24bpp, 8:8:8 */ RGB8 = LDMT1R_MIFTYP_RGB8, /* 24bpp, 8:8:8 */
RGB9, /* 18bpp, 9:9 */ RGB9 = LDMT1R_MIFTYP_RGB9, /* 18bpp, 9:9 */
RGB12A, /* 24bpp, 12:12 */ RGB12A = LDMT1R_MIFTYP_RGB12A, /* 24bpp, 12:12 */
RGB12B, /* 12bpp */ RGB12B = LDMT1R_MIFTYP_RGB12B, /* 12bpp */
RGB16, /* 16bpp */ RGB16 = LDMT1R_MIFTYP_RGB16, /* 16bpp */
RGB18, /* 18bpp */ RGB18 = LDMT1R_MIFTYP_RGB18, /* 18bpp */
RGB24, /* 24bpp */ RGB24 = LDMT1R_MIFTYP_RGB24, /* 24bpp */
YUV422, /* 16bpp */ YUV422 = LDMT1R_MIFTYP_YCBCR, /* 16bpp */
SYS8A, /* 24bpp, 8:8:8 */ SYS8A = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8A, /* 24bpp, 8:8:8 */
SYS8B, /* 18bpp, 8:8:2 */ SYS8B = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8B, /* 18bpp, 8:8:2 */
SYS8C, /* 18bpp, 2:8:8 */ SYS8C = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8C, /* 18bpp, 2:8:8 */
SYS8D, /* 16bpp, 8:8 */ SYS8D = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8D, /* 16bpp, 8:8 */
SYS9, /* 18bpp, 9:9 */ SYS9 = LDMT1R_IFM | LDMT1R_MIFTYP_SYS9, /* 18bpp, 9:9 */
SYS12, /* 24bpp, 12:12 */ SYS12 = LDMT1R_IFM | LDMT1R_MIFTYP_SYS12, /* 24bpp, 12:12 */
SYS16A, /* 16bpp */ SYS16A = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16A, /* 16bpp */
SYS16B, /* 18bpp, 16:2 */ SYS16B = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16B, /* 18bpp, 16:2 */
SYS16C, /* 18bpp, 2:16 */ SYS16C = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16C, /* 18bpp, 2:16 */
SYS18, /* 18bpp */ SYS18 = LDMT1R_IFM | LDMT1R_MIFTYP_SYS18, /* 18bpp */
SYS24, /* 24bpp */ SYS24 = LDMT1R_IFM | LDMT1R_MIFTYP_SYS24, /* 24bpp */
}; };
enum { LCDC_CHAN_DISABLED = 0, enum { LCDC_CHAN_DISABLED = 0,
......
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