Merge branch 'sh-mobile-lcdc' of git://linuxtv.org/pinchartl/fbdev into fbdev-next
This commit is contained in:
commit
8edbeb6eea
|
@ -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, &hdmi_lcdc_device);
|
||||
sh7372_add_device_to_domain(&sh7372_a4lc, &meram_device);
|
||||
sh7372_add_device_to_domain(&sh7372_a4mp, &fsi_device);
|
||||
|
||||
hdmi_init_pm_clock();
|
||||
|
|
|
@ -24,39 +24,14 @@
|
|||
#include <linux/backlight.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <video/sh_mobile_lcdc.h>
|
||||
#include <video/sh_mobile_meram.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#include "sh_mobile_lcdcfb.h"
|
||||
#include "sh_mobile_meram.h"
|
||||
|
||||
#define SIDE_B_OFFSET 0x1000
|
||||
#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_YRES 1080
|
||||
|
||||
|
@ -98,22 +73,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
|
|||
[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 = {
|
||||
.name = "HDMI 720p",
|
||||
.xres = 1280,
|
||||
|
@ -141,7 +100,6 @@ struct sh_mobile_lcdc_priv {
|
|||
unsigned long lddckr;
|
||||
struct sh_mobile_lcdc_chan ch[2];
|
||||
struct notifier_block notifier;
|
||||
unsigned long saved_shared_regs[NR_SHARED_REGS];
|
||||
int started;
|
||||
int forced_bpp; /* 2 channel LCDC must share bpp setting */
|
||||
struct sh_mobile_meram_info *meram_dev;
|
||||
|
@ -218,33 +176,36 @@ static void lcdc_sys_write_index(void *handle, unsigned long data)
|
|||
{
|
||||
struct sh_mobile_lcdc_chan *ch = handle;
|
||||
|
||||
lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000);
|
||||
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
|
||||
lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
|
||||
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
|
||||
lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT);
|
||||
lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
|
||||
lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA |
|
||||
(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)
|
||||
{
|
||||
struct sh_mobile_lcdc_chan *ch = handle;
|
||||
|
||||
lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000);
|
||||
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
|
||||
lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
|
||||
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
|
||||
lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT | LDDWDxR_RSW);
|
||||
lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
|
||||
lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA |
|
||||
(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)
|
||||
{
|
||||
struct sh_mobile_lcdc_chan *ch = handle;
|
||||
|
||||
lcdc_write(ch->lcdc, _LDDRDR, 0x01000000);
|
||||
lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
|
||||
lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
|
||||
lcdc_write(ch->lcdc, _LDDRDR, LDDRDR_RSR);
|
||||
lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
|
||||
lcdc_write(ch->lcdc, _LDDRAR, LDDRAR_RA |
|
||||
(lcdc_chan_is_sublcd(ch) ? 2 : 0));
|
||||
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 = {
|
||||
|
@ -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)
|
||||
{
|
||||
if (atomic_inc_and_test(&priv->hw_usecnt)) {
|
||||
pm_runtime_get_sync(priv->dev);
|
||||
if (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)
|
||||
{
|
||||
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)
|
||||
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,
|
|||
if (bcfg->start_transfer)
|
||||
bcfg->start_transfer(bcfg->board_data, ch,
|
||||
&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);
|
||||
} else {
|
||||
if (bcfg->start_transfer)
|
||||
bcfg->start_transfer(bcfg->board_data, ch,
|
||||
&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)
|
|||
{
|
||||
struct sh_mobile_lcdc_priv *priv = data;
|
||||
struct sh_mobile_lcdc_chan *ch;
|
||||
unsigned long tmp;
|
||||
unsigned long ldintr;
|
||||
int is_sub;
|
||||
int k;
|
||||
|
||||
/* acknowledge interrupt */
|
||||
ldintr = tmp = lcdc_read(priv, _LDINTR);
|
||||
/*
|
||||
* 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);
|
||||
/* Acknowledge interrupts and disable further VSYNC End IRQs. */
|
||||
ldintr = lcdc_read(priv, _LDINTR);
|
||||
lcdc_write(priv, _LDINTR, (ldintr ^ LDINTR_STATUS_MASK) & ~LDINTR_VEE);
|
||||
|
||||
/* 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 */
|
||||
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
||||
|
@ -365,7 +324,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
|
|||
if (!ch->enabled)
|
||||
continue;
|
||||
|
||||
/* Frame Start */
|
||||
/* Frame End */
|
||||
if (ldintr & LDINTR_FS) {
|
||||
if (is_sub == lcdc_chan_is_sublcd(ch)) {
|
||||
ch->frame_end = 1;
|
||||
|
@ -391,16 +350,17 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
|
|||
|
||||
/* start or stop the lcdc */
|
||||
if (start)
|
||||
lcdc_write(priv, _LDCNT2R, tmp | START_LCDC);
|
||||
lcdc_write(priv, _LDCNT2R, tmp | LDCNT2R_DO);
|
||||
else
|
||||
lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC);
|
||||
lcdc_write(priv, _LDCNT2R, tmp & ~LDCNT2R_DO);
|
||||
|
||||
/* wait until power is applied/stopped on all channels */
|
||||
for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
|
||||
if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled)
|
||||
while (1) {
|
||||
tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3;
|
||||
if (start && tmp == 3)
|
||||
tmp = lcdc_read_chan(&priv->ch[k], LDPMR)
|
||||
& LDPMR_LPS;
|
||||
if (start && tmp == LDPMR_LPS)
|
||||
break;
|
||||
if (!start && tmp == 0)
|
||||
break;
|
||||
|
@ -418,13 +378,13 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
|
|||
u32 tmp;
|
||||
|
||||
tmp = ch->ldmt1r_value;
|
||||
tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
|
||||
tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
|
||||
tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
|
||||
tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
|
||||
tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
|
||||
tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
|
||||
tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
|
||||
tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL;
|
||||
tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL;
|
||||
tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
|
||||
tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
|
||||
tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
|
||||
tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
|
||||
tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
|
||||
lcdc_write_chan(ch, LDMT1R, tmp);
|
||||
|
||||
/* setup SYS bus */
|
||||
|
@ -463,242 +423,239 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
|
|||
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_board_cfg *board_cfg;
|
||||
unsigned long tmp;
|
||||
int bpp = 0;
|
||||
unsigned long ldddsr;
|
||||
int k, m, ret;
|
||||
int k, m;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
/* Enable LCDC channels. Read data from external memory, avoid using the
|
||||
* BEU for now.
|
||||
*/
|
||||
lcdc_write(priv, _LDCNT2R, priv->ch[0].enabled | priv->ch[1].enabled);
|
||||
|
||||
/* 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 */
|
||||
lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);
|
||||
|
||||
/* stop the lcdc first */
|
||||
/* Stop the LCDC first and disable all interrupts. */
|
||||
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;
|
||||
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
||||
ch = &priv->ch[k];
|
||||
|
||||
if (!priv->ch[k].enabled)
|
||||
if (!ch->enabled)
|
||||
continue;
|
||||
|
||||
if (!bpp)
|
||||
bpp = ch->info->var.bits_per_pixel;
|
||||
|
||||
/* Power supply */
|
||||
lcdc_write_chan(ch, LDPMR, 0);
|
||||
|
||||
m = ch->cfg.clock_divider;
|
||||
if (!m)
|
||||
continue;
|
||||
|
||||
if (m == 1)
|
||||
m = 1 << 6;
|
||||
tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
|
||||
|
||||
/* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */
|
||||
/* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider
|
||||
* denominator.
|
||||
*/
|
||||
lcdc_write_chan(ch, LDDCKPAT1R, 0);
|
||||
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);
|
||||
|
||||
/* start dotclock again */
|
||||
lcdc_write(priv, _LDDCKSTPR, 0);
|
||||
lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
|
||||
|
||||
/* interrupts are disabled to begin with */
|
||||
lcdc_write(priv, _LDINTR, 0);
|
||||
|
||||
/* Setup geometry, format, frame buffer memory and operation mode. */
|
||||
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
||||
ch = &priv->ch[k];
|
||||
|
||||
if (!ch->enabled)
|
||||
continue;
|
||||
|
||||
sh_mobile_lcdc_geometry(ch);
|
||||
|
||||
/* power supply */
|
||||
lcdc_write_chan(ch, LDPMR, 0);
|
||||
if (ch->info->var.nonstd) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
lcdc_write_chan(ch, LDDFR, tmp);
|
||||
lcdc_write_chan(ch, LDMLSR, ch->pitch);
|
||||
lcdc_write_chan(ch, LDSA1R, ch->base_addr_y);
|
||||
if (ch->info->var.nonstd)
|
||||
lcdc_write_chan(ch, LDSA2R, ch->base_addr_c);
|
||||
|
||||
/* 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. */
|
||||
if (priv->ch[0].info->var.nonstd)
|
||||
tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
|
||||
else {
|
||||
switch (bpp) {
|
||||
case 16:
|
||||
tmp = LDDDSR_LS | LDDDSR_WS;
|
||||
break;
|
||||
case 24:
|
||||
tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
|
||||
break;
|
||||
case 32:
|
||||
default:
|
||||
tmp = LDDDSR_LS;
|
||||
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++) {
|
||||
ch = &priv->ch[k];
|
||||
|
||||
if (!ch->enabled)
|
||||
continue;
|
||||
|
||||
board_cfg = &ch->cfg.board_cfg;
|
||||
if (board_cfg->setup_sys) {
|
||||
ret = board_cfg->setup_sys(board_cfg->board_data,
|
||||
ch, &sh_mobile_lcdc_sys_bus_ops);
|
||||
ret = board_cfg->setup_sys(board_cfg->board_data, ch,
|
||||
&sh_mobile_lcdc_sys_bus_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* word and long word swap */
|
||||
ldddsr = lcdc_read(priv, _LDDDSR);
|
||||
if (priv->ch[0].info->var.nonstd)
|
||||
lcdc_write(priv, _LDDDSR, ldddsr | 7);
|
||||
else {
|
||||
switch (bpp) {
|
||||
case 16:
|
||||
lcdc_write(priv, _LDDDSR, ldddsr | 6);
|
||||
break;
|
||||
case 24:
|
||||
lcdc_write(priv, _LDDDSR, ldddsr | 7);
|
||||
break;
|
||||
case 32:
|
||||
lcdc_write(priv, _LDDDSR, ldddsr | 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute frame buffer base address and pitch for each channel. */
|
||||
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];
|
||||
struct sh_mobile_meram_cfg *cfg;
|
||||
int pixelformat;
|
||||
|
||||
if (!priv->ch[k].enabled)
|
||||
ch = &priv->ch[k];
|
||||
if (!ch->enabled)
|
||||
continue;
|
||||
|
||||
/* set bpp format in PKF[4:0] */
|
||||
tmp = lcdc_read_chan(ch, LDDFR);
|
||||
tmp &= ~0x0003031f;
|
||||
if (ch->info->var.nonstd) {
|
||||
tmp |= (ch->info->var.nonstd << 16);
|
||||
switch (ch->info->var.bits_per_pixel) {
|
||||
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);
|
||||
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;
|
||||
|
||||
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 */
|
||||
if (ch->cfg.meram_cfg && priv->meram_dev &&
|
||||
priv->meram_dev->ops) {
|
||||
struct sh_mobile_meram_cfg *cfg;
|
||||
struct sh_mobile_meram_info *mdev;
|
||||
unsigned long icb_addr_y, icb_addr_c;
|
||||
int icb_pitch;
|
||||
int pf;
|
||||
|
||||
cfg = ch->cfg.meram_cfg;
|
||||
mdev = priv->meram_dev;
|
||||
/* we need to de-init configured ICBs before we
|
||||
* we can re-initialize them.
|
||||
*/
|
||||
if (ch->meram_enabled)
|
||||
mdev->ops->meram_unregister(mdev, cfg);
|
||||
/* Enable MERAM if possible. */
|
||||
cfg = ch->cfg.meram_cfg;
|
||||
if (mdev == NULL || mdev->ops == NULL || cfg == NULL)
|
||||
continue;
|
||||
|
||||
/* we need to de-init configured ICBs before we can
|
||||
* re-initialize them.
|
||||
*/
|
||||
if (ch->meram_enabled) {
|
||||
mdev->ops->meram_unregister(mdev, cfg);
|
||||
ch->meram_enabled = 0;
|
||||
|
||||
if (ch->info->var.nonstd) {
|
||||
if (ch->info->var.bits_per_pixel == 24)
|
||||
pf = SH_MOBILE_MERAM_PF_NV24;
|
||||
else
|
||||
pf = 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;
|
||||
|
||||
ch->meram_enabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* point out our frame buffer */
|
||||
lcdc_write_chan(ch, LDSA1R, base_addr_y);
|
||||
if (ch->info->var.nonstd)
|
||||
lcdc_write_chan(ch, LDSA2R, base_addr_c);
|
||||
if (!ch->info->var.nonstd)
|
||||
pixelformat = SH_MOBILE_MERAM_PF_RGB;
|
||||
else if (ch->info->var.bits_per_pixel == 24)
|
||||
pixelformat = SH_MOBILE_MERAM_PF_NV24;
|
||||
else
|
||||
pixelformat = SH_MOBILE_MERAM_PF_NV;
|
||||
|
||||
/* set line size */
|
||||
lcdc_write_chan(ch, LDMLSR, pitch);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* display output */
|
||||
lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);
|
||||
/* Start the LCDC. */
|
||||
__sh_mobile_lcdc_start(priv);
|
||||
|
||||
/* start the lcdc */
|
||||
sh_mobile_lcdc_start_stop(priv, 1);
|
||||
priv->started = 1;
|
||||
|
||||
/* tell the board code to enable the panel */
|
||||
/* Setup deferred I/O, tell the board code to enable the panels, and
|
||||
* turn backlight on.
|
||||
*/
|
||||
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
||||
ch = &priv->ch[k];
|
||||
if (!ch->enabled)
|
||||
continue;
|
||||
|
||||
tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
|
||||
if (ch->ldmt1r_value & LDMT1R_IFM && 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);
|
||||
}
|
||||
|
||||
board_cfg = &ch->cfg.board_cfg;
|
||||
if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
|
||||
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)
|
|||
|
||||
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) {
|
||||
case RGB8: ifm = 0; miftyp = 0; break;
|
||||
case RGB9: ifm = 0; miftyp = 4; break;
|
||||
case RGB12A: ifm = 0; miftyp = 5; break;
|
||||
case RGB12B: ifm = 0; miftyp = 6; break;
|
||||
case RGB16: ifm = 0; miftyp = 7; break;
|
||||
case RGB18: ifm = 0; miftyp = 10; break;
|
||||
case RGB24: ifm = 0; miftyp = 11; break;
|
||||
case SYS8A: ifm = 1; miftyp = 0; break;
|
||||
case SYS8B: ifm = 1; miftyp = 1; break;
|
||||
case SYS8C: ifm = 1; miftyp = 2; break;
|
||||
case SYS8D: ifm = 1; miftyp = 3; break;
|
||||
case SYS9: ifm = 1; miftyp = 4; break;
|
||||
case SYS12: ifm = 1; miftyp = 5; break;
|
||||
case SYS16A: ifm = 1; miftyp = 7; break;
|
||||
case SYS16B: ifm = 1; miftyp = 8; break;
|
||||
case SYS16C: ifm = 1; miftyp = 9; break;
|
||||
case SYS18: ifm = 1; miftyp = 10; break;
|
||||
case SYS24: ifm = 1; miftyp = 11; break;
|
||||
default: goto bad;
|
||||
switch (interface_type) {
|
||||
case RGB8:
|
||||
case RGB9:
|
||||
case RGB12A:
|
||||
case RGB12B:
|
||||
case RGB16:
|
||||
case RGB18:
|
||||
case RGB24:
|
||||
case SYS8A:
|
||||
case SYS8B:
|
||||
case SYS8C:
|
||||
case SYS8D:
|
||||
case SYS9:
|
||||
case SYS12:
|
||||
case SYS16A:
|
||||
case SYS16B:
|
||||
case SYS16C:
|
||||
case SYS18:
|
||||
case SYS24:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* SUBLCD only supports SYS interface */
|
||||
if (lcdc_chan_is_sublcd(ch)) {
|
||||
if (ifm == 0)
|
||||
goto bad;
|
||||
else
|
||||
ifm = 0;
|
||||
if (!(interface_type & LDMT1R_IFM))
|
||||
return -EINVAL;
|
||||
|
||||
interface_type &= ~LDMT1R_IFM;
|
||||
}
|
||||
|
||||
ch->ldmt1r_value = (ifm << 12) | miftyp;
|
||||
ch->ldmt1r_value = interface_type;
|
||||
return 0;
|
||||
bad:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
char *str;
|
||||
int icksel;
|
||||
|
||||
switch (clock_source) {
|
||||
case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break;
|
||||
case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break;
|
||||
case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break;
|
||||
case LCDC_CLK_BUS:
|
||||
str = "bus_clk";
|
||||
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:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->lddckr = icksel << 16;
|
||||
|
||||
if (str) {
|
||||
priv->dot_clk = clk_get(&pdev->dev, str);
|
||||
if (IS_ERR(priv->dot_clk)) {
|
||||
|
@ -940,33 +903,29 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
|
|||
base_addr_c += 2 * var->xoffset;
|
||||
else
|
||||
base_addr_c += var->xoffset;
|
||||
} else
|
||||
base_addr_c = 0;
|
||||
}
|
||||
|
||||
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 {
|
||||
if (ch->meram_enabled) {
|
||||
struct sh_mobile_meram_cfg *cfg;
|
||||
struct sh_mobile_meram_info *mdev;
|
||||
unsigned long icb_addr_y, icb_addr_c;
|
||||
int ret;
|
||||
|
||||
cfg = ch->cfg.meram_cfg;
|
||||
mdev = priv->meram_dev;
|
||||
ret = mdev->ops->meram_update(mdev, cfg,
|
||||
base_addr_y, base_addr_c,
|
||||
&icb_addr_y, &icb_addr_c);
|
||||
&base_addr_y, &base_addr_c);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
lcdc_write_chan_mirror(ch, LDSA1R, icb_addr_y);
|
||||
if (icb_addr_c)
|
||||
lcdc_write_chan_mirror(ch, LDSA2R, icb_addr_c);
|
||||
|
||||
}
|
||||
|
||||
ch->base_addr_y = base_addr_y;
|
||||
ch->base_addr_c = base_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))
|
||||
lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
|
||||
else
|
||||
|
@ -985,9 +944,11 @@ static int sh_mobile_wait_for_vsync(struct fb_info *info)
|
|||
unsigned long ldintr;
|
||||
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 |= LDINTR_VEE;
|
||||
ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK;
|
||||
lcdc_write(ch->lcdc, _LDINTR, ldintr);
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
|
||||
|
@ -1316,47 +1277,20 @@ static int sh_mobile_lcdc_resume(struct device *dev)
|
|||
static int sh_mobile_lcdc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sh_mobile_lcdc_priv *p = 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]);
|
||||
struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
/* turn off LCDC hardware */
|
||||
lcdc_write(p, _LDCNT1R, 0);
|
||||
lcdc_write(priv, _LDCNT1R, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_mobile_lcdc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
|
||||
struct sh_mobile_lcdc_chan *ch;
|
||||
int k, n;
|
||||
struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
/* 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 */
|
||||
for (n = 0; n < NR_SHARED_REGS; n++)
|
||||
lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]);
|
||||
__sh_mobile_lcdc_start(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1472,12 +1406,12 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
|
|||
|
||||
switch (pdata->ch[i].chan) {
|
||||
case LCDC_CHAN_MAINLCD:
|
||||
ch->enabled = 1 << 1;
|
||||
ch->enabled = LDCNT2R_ME;
|
||||
ch->reg_offs = lcdc_offs_mainlcd;
|
||||
j++;
|
||||
break;
|
||||
case LCDC_CHAN_SUBLCD:
|
||||
ch->enabled = 1 << 2;
|
||||
ch->enabled = LDCNT2R_SE;
|
||||
ch->reg_offs = lcdc_offs_sublcd;
|
||||
j++;
|
||||
break;
|
||||
|
|
|
@ -18,6 +18,13 @@ struct sh_mobile_lcdc_priv;
|
|||
struct fb_info;
|
||||
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_priv *lcdc;
|
||||
unsigned long *reg_offs;
|
||||
|
@ -25,7 +32,6 @@ struct sh_mobile_lcdc_chan {
|
|||
unsigned long enabled; /* ME and SE in LDCNT2R */
|
||||
struct sh_mobile_lcdc_chan_cfg cfg;
|
||||
u32 pseudo_palette[PALETTE_NR];
|
||||
unsigned long saved_ch_regs[NR_CH_REGS];
|
||||
struct fb_info *info;
|
||||
struct backlight_device *bl;
|
||||
dma_addr_t dma_handle;
|
||||
|
@ -40,6 +46,10 @@ struct sh_mobile_lcdc_chan {
|
|||
int blank_status;
|
||||
struct mutex open_lock; /* protects the use counter */
|
||||
int meram_enabled;
|
||||
|
||||
unsigned long base_addr_y;
|
||||
unsigned long base_addr_c;
|
||||
unsigned int pitch;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,29 +12,103 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "sh_mobile_meram.h"
|
||||
#include <video/sh_mobile_meram.h>
|
||||
|
||||
/* meram registers */
|
||||
#define MExxCTL 0x0
|
||||
#define MExxBSIZE 0x4
|
||||
#define MExxMNCF 0x8
|
||||
#define MExxSARA 0x10
|
||||
#define MExxSARB 0x14
|
||||
#define MExxSBSIZE 0x18
|
||||
#define MEVCR1 0x4
|
||||
#define MEVCR1_RST (1 << 31)
|
||||
#define MEVCR1_WD (1 << 30)
|
||||
#define MEVCR1_AMD1 (1 << 29)
|
||||
#define MEVCR1_AMD0 (1 << 28)
|
||||
#define MEQSEL1 0x40
|
||||
#define MEQSEL2 0x44
|
||||
|
||||
#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 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 MEVCR1 0x4
|
||||
#define MEACTS 0x10
|
||||
#define MEQSEL1 0x40
|
||||
#define MEQSEL2 0x44
|
||||
#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 */
|
||||
#define MERAM_SEC_LINE 15
|
||||
|
@ -44,8 +118,7 @@
|
|||
* MERAM/ICB access functions
|
||||
*/
|
||||
|
||||
#define MERAM_ICB_OFFSET(base, idx, off) \
|
||||
((base) + (0x400 + ((idx) * 0x20) + (off)))
|
||||
#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
|
||||
|
||||
static inline void meram_write_icb(void __iomem *base, int idx, int off,
|
||||
unsigned long val)
|
||||
|
@ -280,17 +353,18 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
|
|||
/*
|
||||
* 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 split the allocated MERAM buffer between two ICBs.
|
||||
*/
|
||||
meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
|
||||
MERAM_MExxCTL_VAL(0x70f, icb->marker_icb,
|
||||
icb->meram_offset));
|
||||
MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) |
|
||||
MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
|
||||
MExxCTL_MD_FB);
|
||||
meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
|
||||
MERAM_MExxCTL_VAL(0x70f, icb->cache_icb,
|
||||
icb->meram_offset +
|
||||
icb->meram_size / 2));
|
||||
MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset +
|
||||
icb->meram_size / 2) |
|
||||
MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
|
||||
MExxCTL_MD_FB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -337,24 +411,22 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
|
|||
xres, yres, (!pixelformat) ? "yuv" : "rgb",
|
||||
base_addr_y, base_addr_c);
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
/* we can't handle wider than 8192px */
|
||||
if (xres > 8192) {
|
||||
dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
|
||||
error = -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;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* do we have at least one ICB config? */
|
||||
if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
|
||||
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;
|
||||
goto err;
|
||||
}
|
||||
|
@ -460,6 +532,57 @@ static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
|
|||
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 = {
|
||||
.module = THIS_MODULE,
|
||||
.meram_register = sh_mobile_meram_register,
|
||||
|
@ -513,7 +636,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
|
|||
|
||||
/* initialize ICB addressing mode */
|
||||
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.");
|
||||
|
||||
|
@ -530,6 +655,8 @@ static int sh_mobile_meram_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
if (priv->base)
|
||||
iounmap(priv->base);
|
||||
|
||||
|
@ -544,6 +671,7 @@ static struct platform_driver sh_mobile_meram_driver = {
|
|||
.driver = {
|
||||
.name = "sh_mobile_meram",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &sh_mobile_meram_dev_pm_ops,
|
||||
},
|
||||
.probe = sh_mobile_meram_probe,
|
||||
.remove = sh_mobile_meram_remove,
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
#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 @@
|
|||
#include <linux/fb.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 {
|
||||
RGB8, /* 24bpp, 8:8:8 */
|
||||
RGB9, /* 18bpp, 9:9 */
|
||||
RGB12A, /* 24bpp, 12:12 */
|
||||
RGB12B, /* 12bpp */
|
||||
RGB16, /* 16bpp */
|
||||
RGB18, /* 18bpp */
|
||||
RGB24, /* 24bpp */
|
||||
YUV422, /* 16bpp */
|
||||
SYS8A, /* 24bpp, 8:8:8 */
|
||||
SYS8B, /* 18bpp, 8:8:2 */
|
||||
SYS8C, /* 18bpp, 2:8:8 */
|
||||
SYS8D, /* 16bpp, 8:8 */
|
||||
SYS9, /* 18bpp, 9:9 */
|
||||
SYS12, /* 24bpp, 12:12 */
|
||||
SYS16A, /* 16bpp */
|
||||
SYS16B, /* 18bpp, 16:2 */
|
||||
SYS16C, /* 18bpp, 2:16 */
|
||||
SYS18, /* 18bpp */
|
||||
SYS24, /* 24bpp */
|
||||
RGB8 = LDMT1R_MIFTYP_RGB8, /* 24bpp, 8:8:8 */
|
||||
RGB9 = LDMT1R_MIFTYP_RGB9, /* 18bpp, 9:9 */
|
||||
RGB12A = LDMT1R_MIFTYP_RGB12A, /* 24bpp, 12:12 */
|
||||
RGB12B = LDMT1R_MIFTYP_RGB12B, /* 12bpp */
|
||||
RGB16 = LDMT1R_MIFTYP_RGB16, /* 16bpp */
|
||||
RGB18 = LDMT1R_MIFTYP_RGB18, /* 18bpp */
|
||||
RGB24 = LDMT1R_MIFTYP_RGB24, /* 24bpp */
|
||||
YUV422 = LDMT1R_MIFTYP_YCBCR, /* 16bpp */
|
||||
SYS8A = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8A, /* 24bpp, 8:8:8 */
|
||||
SYS8B = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8B, /* 18bpp, 8:8:2 */
|
||||
SYS8C = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8C, /* 18bpp, 2:8:8 */
|
||||
SYS8D = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8D, /* 16bpp, 8:8 */
|
||||
SYS9 = LDMT1R_IFM | LDMT1R_MIFTYP_SYS9, /* 18bpp, 9:9 */
|
||||
SYS12 = LDMT1R_IFM | LDMT1R_MIFTYP_SYS12, /* 24bpp, 12:12 */
|
||||
SYS16A = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16A, /* 16bpp */
|
||||
SYS16B = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16B, /* 18bpp, 16:2 */
|
||||
SYS16C = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16C, /* 18bpp, 2:16 */
|
||||
SYS18 = LDMT1R_IFM | LDMT1R_MIFTYP_SYS18, /* 18bpp */
|
||||
SYS24 = LDMT1R_IFM | LDMT1R_MIFTYP_SYS24, /* 24bpp */
|
||||
};
|
||||
|
||||
enum { LCDC_CHAN_DISABLED = 0,
|
||||
|
|
Loading…
Reference in New Issue