Commit 8dc50ec7 authored by Tomi Valkeinen's avatar Tomi Valkeinen

OMAPFB: Remove OMAP2/3 support from old omapfb driver

Old omapfb driver (drivers/video/omap/) is no longer used for OMAP2+
devices, and thus we can remove OMAP2+ support from it and make it an
OMAP1 omapfb driver.
Signed-off-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
parent fdcb6888
config FB_OMAP config FB_OMAP
tristate "OMAP frame buffer support (EXPERIMENTAL)" tristate "OMAP frame buffer support (EXPERIMENTAL)"
depends on FB && (OMAP2_DSS = "n") depends on FB
depends on ARCH_OMAP1 || ARCH_OMAP2 || ARCH_OMAP3 depends on ARCH_OMAP1
select FB_CFB_FILLRECT select FB_CFB_FILLRECT
select FB_CFB_COPYAREA select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT select FB_CFB_IMAGEBLIT
select TWL4030_CORE if MACH_OMAP_2430SDP
help help
Frame buffer driver for OMAP based boards. Frame buffer driver for OMAP based boards.
...@@ -42,7 +41,7 @@ config FB_OMAP_LCD_MIPID ...@@ -42,7 +41,7 @@ config FB_OMAP_LCD_MIPID
config FB_OMAP_BOOTLOADER_INIT config FB_OMAP_BOOTLOADER_INIT
bool "Check bootloader initialization" bool "Check bootloader initialization"
depends on FB_OMAP || FB_OMAP2 depends on FB_OMAP
help help
Say Y here if you want to enable checking if the bootloader has Say Y here if you want to enable checking if the bootloader has
already initialized the display controller. In this case the already initialized the display controller. In this case the
...@@ -61,7 +60,7 @@ config FB_OMAP_CONSISTENT_DMA_SIZE ...@@ -61,7 +60,7 @@ config FB_OMAP_CONSISTENT_DMA_SIZE
config FB_OMAP_DMA_TUNE config FB_OMAP_DMA_TUNE
bool "Set DMA SDRAM access priority high" bool "Set DMA SDRAM access priority high"
depends on FB_OMAP && ARCH_OMAP1 depends on FB_OMAP
help help
On systems in which video memory is in system memory On systems in which video memory is in system memory
(SDRAM) this will speed up graphics DMA operations. (SDRAM) this will speed up graphics DMA operations.
......
# #
# Makefile for the new OMAP framebuffer device driver # Makefile for the OMAP1 framebuffer device driver
# #
obj-$(CONFIG_FB_OMAP) += omapfb.o obj-$(CONFIG_FB_OMAP) += omapfb.o
...@@ -7,11 +7,8 @@ obj-$(CONFIG_FB_OMAP) += omapfb.o ...@@ -7,11 +7,8 @@ obj-$(CONFIG_FB_OMAP) += omapfb.o
objs-yy := omapfb_main.o objs-yy := omapfb_main.o
objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o
objs-y$(CONFIG_ARCH_OMAP2) += dispc.o
objs-y$(CONFIG_ARCH_OMAP3) += dispc.o
objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o
objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o
objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o
......
/*
* OMAP2 display controller support
*
* Copyright (C) 2005 Nokia Corporation
* Author: Imre Deak <imre.deak@nokia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <plat/sram.h>
#include <plat/board.h>
#include "omapfb.h"
#include "dispc.h"
#define MODULE_NAME "dispc"
#define DSS_BASE 0x48050000
#define DSS_SYSCONFIG 0x0010
#define DISPC_BASE 0x48050400
/* DISPC common */
#define DISPC_REVISION 0x0000
#define DISPC_SYSCONFIG 0x0010
#define DISPC_SYSSTATUS 0x0014
#define DISPC_IRQSTATUS 0x0018
#define DISPC_IRQENABLE 0x001C
#define DISPC_CONTROL 0x0040
#define DISPC_CONFIG 0x0044
#define DISPC_CAPABLE 0x0048
#define DISPC_DEFAULT_COLOR0 0x004C
#define DISPC_DEFAULT_COLOR1 0x0050
#define DISPC_TRANS_COLOR0 0x0054
#define DISPC_TRANS_COLOR1 0x0058
#define DISPC_LINE_STATUS 0x005C
#define DISPC_LINE_NUMBER 0x0060
#define DISPC_TIMING_H 0x0064
#define DISPC_TIMING_V 0x0068
#define DISPC_POL_FREQ 0x006C
#define DISPC_DIVISOR 0x0070
#define DISPC_SIZE_DIG 0x0078
#define DISPC_SIZE_LCD 0x007C
#define DISPC_DATA_CYCLE1 0x01D4
#define DISPC_DATA_CYCLE2 0x01D8
#define DISPC_DATA_CYCLE3 0x01DC
/* DISPC GFX plane */
#define DISPC_GFX_BA0 0x0080
#define DISPC_GFX_BA1 0x0084
#define DISPC_GFX_POSITION 0x0088
#define DISPC_GFX_SIZE 0x008C
#define DISPC_GFX_ATTRIBUTES 0x00A0
#define DISPC_GFX_FIFO_THRESHOLD 0x00A4
#define DISPC_GFX_FIFO_SIZE_STATUS 0x00A8
#define DISPC_GFX_ROW_INC 0x00AC
#define DISPC_GFX_PIXEL_INC 0x00B0
#define DISPC_GFX_WINDOW_SKIP 0x00B4
#define DISPC_GFX_TABLE_BA 0x00B8
/* DISPC Video plane 1/2 */
#define DISPC_VID1_BASE 0x00BC
#define DISPC_VID2_BASE 0x014C
/* Offsets into DISPC_VID1/2_BASE */
#define DISPC_VID_BA0 0x0000
#define DISPC_VID_BA1 0x0004
#define DISPC_VID_POSITION 0x0008
#define DISPC_VID_SIZE 0x000C
#define DISPC_VID_ATTRIBUTES 0x0010
#define DISPC_VID_FIFO_THRESHOLD 0x0014
#define DISPC_VID_FIFO_SIZE_STATUS 0x0018
#define DISPC_VID_ROW_INC 0x001C
#define DISPC_VID_PIXEL_INC 0x0020
#define DISPC_VID_FIR 0x0024
#define DISPC_VID_PICTURE_SIZE 0x0028
#define DISPC_VID_ACCU0 0x002C
#define DISPC_VID_ACCU1 0x0030
/* 8 elements in 8 byte increments */
#define DISPC_VID_FIR_COEF_H0 0x0034
/* 8 elements in 8 byte increments */
#define DISPC_VID_FIR_COEF_HV0 0x0038
/* 5 elements in 4 byte increments */
#define DISPC_VID_CONV_COEF0 0x0074
#define DISPC_IRQ_FRAMEMASK 0x0001
#define DISPC_IRQ_VSYNC 0x0002
#define DISPC_IRQ_EVSYNC_EVEN 0x0004
#define DISPC_IRQ_EVSYNC_ODD 0x0008
#define DISPC_IRQ_ACBIAS_COUNT_STAT 0x0010
#define DISPC_IRQ_PROG_LINE_NUM 0x0020
#define DISPC_IRQ_GFX_FIFO_UNDERFLOW 0x0040
#define DISPC_IRQ_GFX_END_WIN 0x0080
#define DISPC_IRQ_PAL_GAMMA_MASK 0x0100
#define DISPC_IRQ_OCP_ERR 0x0200
#define DISPC_IRQ_VID1_FIFO_UNDERFLOW 0x0400
#define DISPC_IRQ_VID1_END_WIN 0x0800
#define DISPC_IRQ_VID2_FIFO_UNDERFLOW 0x1000
#define DISPC_IRQ_VID2_END_WIN 0x2000
#define DISPC_IRQ_SYNC_LOST 0x4000
#define DISPC_IRQ_MASK_ALL 0x7fff
#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
DISPC_IRQ_SYNC_LOST)
#define RFBI_CONTROL 0x48050040
#define MAX_PALETTE_SIZE (256 * 16)
#define FLD_MASK(pos, len) (((1 << len) - 1) << pos)
#define MOD_REG_FLD(reg, mask, val) \
dispc_write_reg((reg), (dispc_read_reg(reg) & ~(mask)) | (val));
#define OMAP2_SRAM_START 0x40200000
/* Maximum size, in reality this is smaller if SRAM is partially locked. */
#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */
/* We support the SDRAM / SRAM types. See OMAPFB_PLANE_MEMTYPE_* in omapfb.h */
#define DISPC_MEMTYPE_NUM 2
#define RESMAP_SIZE(_page_cnt) \
((_page_cnt + (sizeof(unsigned long) * 8) - 1) / 8)
#define RESMAP_PTR(_res_map, _page_nr) \
(((_res_map)->map) + (_page_nr) / (sizeof(unsigned long) * 8))
#define RESMAP_MASK(_page_nr) \
(1 << ((_page_nr) & (sizeof(unsigned long) * 8 - 1)))
struct resmap {
unsigned long start;
unsigned page_cnt;
unsigned long *map;
};
#define MAX_IRQ_HANDLERS 4
static struct {
void __iomem *base;
struct omapfb_mem_desc mem_desc;
struct resmap *res_map[DISPC_MEMTYPE_NUM];
atomic_t map_count[OMAPFB_PLANE_NUM];
dma_addr_t palette_paddr;
void *palette_vaddr;
int ext_mode;
struct {
u32 irq_mask;
void (*callback)(void *);
void *data;
} irq_handlers[MAX_IRQ_HANDLERS];
struct completion frame_done;
int fir_hinc[OMAPFB_PLANE_NUM];
int fir_vinc[OMAPFB_PLANE_NUM];
struct clk *dss_ick, *dss1_fck;
struct clk *dss_54m_fck;
enum omapfb_update_mode update_mode;
struct omapfb_device *fbdev;
struct omapfb_color_key color_key;
} dispc;
static void enable_lcd_clocks(int enable);
static void inline dispc_write_reg(int idx, u32 val)
{
__raw_writel(val, dispc.base + idx);
}
static u32 inline dispc_read_reg(int idx)
{
u32 l = __raw_readl(dispc.base + idx);
return l;
}
/* Select RFBI or bypass mode */
static void enable_rfbi_mode(int enable)
{
void __iomem *rfbi_control;
u32 l;
l = dispc_read_reg(DISPC_CONTROL);
/* Enable RFBI, GPIO0/1 */
l &= ~((1 << 11) | (1 << 15) | (1 << 16));
l |= enable ? (1 << 11) : 0;
/* RFBI En: GPIO0/1=10 RFBI Dis: GPIO0/1=11 */
l |= 1 << 15;
l |= enable ? 0 : (1 << 16);
dispc_write_reg(DISPC_CONTROL, l);
/* Set bypass mode in RFBI module */
rfbi_control = ioremap(RFBI_CONTROL, SZ_1K);
if (!rfbi_control) {
pr_err("Unable to ioremap rfbi_control\n");
return;
}
l = __raw_readl(rfbi_control);
l |= enable ? 0 : (1 << 1);
__raw_writel(l, rfbi_control);
iounmap(rfbi_control);
}
static void set_lcd_data_lines(int data_lines)
{
u32 l;
int code = 0;
switch (data_lines) {
case 12:
code = 0;
break;
case 16:
code = 1;
break;
case 18:
code = 2;
break;
case 24:
code = 3;
break;
default:
BUG();
}
l = dispc_read_reg(DISPC_CONTROL);
l &= ~(0x03 << 8);
l |= code << 8;
dispc_write_reg(DISPC_CONTROL, l);
}
static void set_load_mode(int mode)
{
BUG_ON(mode & ~(DISPC_LOAD_CLUT_ONLY | DISPC_LOAD_FRAME_ONLY |
DISPC_LOAD_CLUT_ONCE_FRAME));
MOD_REG_FLD(DISPC_CONFIG, 0x03 << 1, mode << 1);
}
void omap_dispc_set_lcd_size(int x, int y)
{
BUG_ON((x > (1 << 11)) || (y > (1 << 11)));
enable_lcd_clocks(1);
MOD_REG_FLD(DISPC_SIZE_LCD, FLD_MASK(16, 11) | FLD_MASK(0, 11),
((y - 1) << 16) | (x - 1));
enable_lcd_clocks(0);
}
EXPORT_SYMBOL(omap_dispc_set_lcd_size);
void omap_dispc_set_digit_size(int x, int y)
{
BUG_ON((x > (1 << 11)) || (y > (1 << 11)));
enable_lcd_clocks(1);
MOD_REG_FLD(DISPC_SIZE_DIG, FLD_MASK(16, 11) | FLD_MASK(0, 11),
((y - 1) << 16) | (x - 1));
enable_lcd_clocks(0);
}
EXPORT_SYMBOL(omap_dispc_set_digit_size);
static void setup_plane_fifo(int plane, int ext_mode)
{
const u32 ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD,
DISPC_VID1_BASE + DISPC_VID_FIFO_THRESHOLD,
DISPC_VID2_BASE + DISPC_VID_FIFO_THRESHOLD };
const u32 fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS,
DISPC_VID1_BASE + DISPC_VID_FIFO_SIZE_STATUS,
DISPC_VID2_BASE + DISPC_VID_FIFO_SIZE_STATUS };
int low, high;
u32 l;
BUG_ON(plane > 2);
l = dispc_read_reg(fsz_reg[plane]);
l &= FLD_MASK(0, 11);
if (ext_mode) {
low = l * 3 / 4;
high = l;
} else {
low = l / 4;
high = l * 3 / 4;
}
MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 12) | FLD_MASK(0, 12),
(high << 16) | low);
}
void omap_dispc_enable_lcd_out(int enable)
{
enable_lcd_clocks(1);
MOD_REG_FLD(DISPC_CONTROL, 1, enable ? 1 : 0);
enable_lcd_clocks(0);
}
EXPORT_SYMBOL(omap_dispc_enable_lcd_out);
void omap_dispc_enable_digit_out(int enable)
{
enable_lcd_clocks(1);
MOD_REG_FLD(DISPC_CONTROL, 1 << 1, enable ? 1 << 1 : 0);
enable_lcd_clocks(0);
}
EXPORT_SYMBOL(omap_dispc_enable_digit_out);
static inline int _setup_plane(int plane, int channel_out,
u32 paddr, int screen_width,
int pos_x, int pos_y, int width, int height,
int color_mode)
{
const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES,
DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES,
DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES };
const u32 ba_reg[] = { DISPC_GFX_BA0, DISPC_VID1_BASE + DISPC_VID_BA0,
DISPC_VID2_BASE + DISPC_VID_BA0 };
const u32 ps_reg[] = { DISPC_GFX_POSITION,
DISPC_VID1_BASE + DISPC_VID_POSITION,
DISPC_VID2_BASE + DISPC_VID_POSITION };
const u32 sz_reg[] = { DISPC_GFX_SIZE,
DISPC_VID1_BASE + DISPC_VID_PICTURE_SIZE,
DISPC_VID2_BASE + DISPC_VID_PICTURE_SIZE };
const u32 ri_reg[] = { DISPC_GFX_ROW_INC,
DISPC_VID1_BASE + DISPC_VID_ROW_INC,
DISPC_VID2_BASE + DISPC_VID_ROW_INC };
const u32 vs_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE,
DISPC_VID2_BASE + DISPC_VID_SIZE };
int chout_shift, burst_shift;
int chout_val;
int color_code;
int bpp;
int cconv_en;
int set_vsize;
u32 l;
#ifdef VERBOSE
dev_dbg(dispc.fbdev->dev, "plane %d channel %d paddr %#08x scr_width %d"
" pos_x %d pos_y %d width %d height %d color_mode %d\n",
plane, channel_out, paddr, screen_width, pos_x, pos_y,
width, height, color_mode);
#endif
set_vsize = 0;
switch (plane) {
case OMAPFB_PLANE_GFX:
burst_shift = 6;
chout_shift = 8;
break;
case OMAPFB_PLANE_VID1:
case OMAPFB_PLANE_VID2:
burst_shift = 14;
chout_shift = 16;
set_vsize = 1;
break;
default:
return -EINVAL;
}
switch (channel_out) {
case OMAPFB_CHANNEL_OUT_LCD:
chout_val = 0;
break;
case OMAPFB_CHANNEL_OUT_DIGIT:
chout_val = 1;
break;
default:
return -EINVAL;
}
cconv_en = 0;
switch (color_mode) {
case OMAPFB_COLOR_RGB565:
color_code = DISPC_RGB_16_BPP;
bpp = 16;
break;
case OMAPFB_COLOR_YUV422:
if (plane == 0)
return -EINVAL;
color_code = DISPC_UYVY_422;
cconv_en = 1;
bpp = 16;
break;
case OMAPFB_COLOR_YUY422:
if (plane == 0)
return -EINVAL;
color_code = DISPC_YUV2_422;
cconv_en = 1;
bpp = 16;
break;
default:
return -EINVAL;
}
l = dispc_read_reg(at_reg[plane]);
l &= ~(0x0f << 1);
l |= color_code << 1;
l &= ~(1 << 9);
l |= cconv_en << 9;
l &= ~(0x03 << burst_shift);
l |= DISPC_BURST_8x32 << burst_shift;
l &= ~(1 << chout_shift);
l |= chout_val << chout_shift;
dispc_write_reg(at_reg[plane], l);
dispc_write_reg(ba_reg[plane], paddr);
MOD_REG_FLD(ps_reg[plane],
FLD_MASK(16, 11) | FLD_MASK(0, 11), (pos_y << 16) | pos_x);
MOD_REG_FLD(sz_reg[plane], FLD_MASK(16, 11) | FLD_MASK(0, 11),
((height - 1) << 16) | (width - 1));
if (set_vsize) {
/* Set video size if set_scale hasn't set it */
if (!dispc.fir_vinc[plane])
MOD_REG_FLD(vs_reg[plane],
FLD_MASK(16, 11), (height - 1) << 16);
if (!dispc.fir_hinc[plane])
MOD_REG_FLD(vs_reg[plane],
FLD_MASK(0, 11), width - 1);
}
dispc_write_reg(ri_reg[plane], (screen_width - width) * bpp / 8 + 1);
return height * screen_width * bpp / 8;
}
static int omap_dispc_setup_plane(int plane, int channel_out,
unsigned long offset,
int screen_width,
int pos_x, int pos_y, int width, int height,
int color_mode)
{
u32 paddr;
int r;
if ((unsigned)plane > dispc.mem_desc.region_cnt)
return -EINVAL;
paddr = dispc.mem_desc.region[plane].paddr + offset;
enable_lcd_clocks(1);
r = _setup_plane(plane, channel_out, paddr,
screen_width,
pos_x, pos_y, width, height, color_mode);
enable_lcd_clocks(0);
return r;
}
static void write_firh_reg(int plane, int reg, u32 value)
{
u32 base;
if (plane == 1)
base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_H0;
else
base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_H0;
dispc_write_reg(base + reg * 8, value);
}
static void write_firhv_reg(int plane, int reg, u32 value)
{
u32 base;
if (plane == 1)
base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_HV0;
else
base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_HV0;
dispc_write_reg(base + reg * 8, value);
}
static void set_upsampling_coef_table(int plane)
{
const u32 coef[][2] = {
{ 0x00800000, 0x00800000 },
{ 0x0D7CF800, 0x037B02FF },
{ 0x1E70F5FF, 0x0C6F05FE },
{ 0x335FF5FE, 0x205907FB },
{ 0xF74949F7, 0x00404000 },
{ 0xF55F33FB, 0x075920FE },
{ 0xF5701EFE, 0x056F0CFF },
{ 0xF87C0DFF, 0x027B0300 },
};
int i;
for (i = 0; i < 8; i++) {
write_firh_reg(plane, i, coef[i][0]);
write_firhv_reg(plane, i, coef[i][1]);
}
}
static int omap_dispc_set_scale(int plane,
int orig_width, int orig_height,
int out_width, int out_height)
{
const u32 at_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES,
DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES };
const u32 vs_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE,
DISPC_VID2_BASE + DISPC_VID_SIZE };
const u32 fir_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_FIR,
DISPC_VID2_BASE + DISPC_VID_FIR };
u32 l;
int fir_hinc;
int fir_vinc;
if ((unsigned)plane > OMAPFB_PLANE_NUM)
return -ENODEV;
if (plane == OMAPFB_PLANE_GFX &&
(out_width != orig_width || out_height != orig_height))
return -EINVAL;
enable_lcd_clocks(1);
if (orig_width < out_width) {
/*
* Upsampling.
* Currently you can only scale both dimensions in one way.
*/
if (orig_height > out_height ||
orig_width * 8 < out_width ||
orig_height * 8 < out_height) {
enable_lcd_clocks(0);
return -EINVAL;
}
set_upsampling_coef_table(plane);
} else if (orig_width > out_width) {
/* Downsampling not yet supported
*/
enable_lcd_clocks(0);
return -EINVAL;
}
if (!orig_width || orig_width == out_width)
fir_hinc = 0;
else
fir_hinc = 1024 * orig_width / out_width;
if (!orig_height || orig_height == out_height)
fir_vinc = 0;
else
fir_vinc = 1024 * orig_height / out_height;
dispc.fir_hinc[plane] = fir_hinc;
dispc.fir_vinc[plane] = fir_vinc;
MOD_REG_FLD(fir_reg[plane],
FLD_MASK(16, 12) | FLD_MASK(0, 12),
((fir_vinc & 4095) << 16) |
(fir_hinc & 4095));
dev_dbg(dispc.fbdev->dev, "out_width %d out_height %d orig_width %d "
"orig_height %d fir_hinc %d fir_vinc %d\n",
out_width, out_height, orig_width, orig_height,
fir_hinc, fir_vinc);
MOD_REG_FLD(vs_reg[plane],
FLD_MASK(16, 11) | FLD_MASK(0, 11),
((out_height - 1) << 16) | (out_width - 1));
l = dispc_read_reg(at_reg[plane]);
l &= ~(0x03 << 5);
l |= fir_hinc ? (1 << 5) : 0;
l |= fir_vinc ? (1 << 6) : 0;
dispc_write_reg(at_reg[plane], l);
enable_lcd_clocks(0);
return 0;
}
static int omap_dispc_enable_plane(int plane, int enable)
{
const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES,
DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES,
DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES };
if ((unsigned int)plane > dispc.mem_desc.region_cnt)
return -EINVAL;
enable_lcd_clocks(1);
MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0);
enable_lcd_clocks(0);
return 0;
}
static int omap_dispc_set_color_key(struct omapfb_color_key *ck)
{
u32 df_reg, tr_reg;
int shift, val;
switch (ck->channel_out) {
case OMAPFB_CHANNEL_OUT_LCD:
df_reg = DISPC_DEFAULT_COLOR0;
tr_reg = DISPC_TRANS_COLOR0;
shift = 10;
break;
case OMAPFB_CHANNEL_OUT_DIGIT:
df_reg = DISPC_DEFAULT_COLOR1;
tr_reg = DISPC_TRANS_COLOR1;
shift = 12;
break;
default:
return -EINVAL;
}
switch (ck->key_type) {
case OMAPFB_COLOR_KEY_DISABLED:
val = 0;
break;
case OMAPFB_COLOR_KEY_GFX_DST:
val = 1;
break;
case OMAPFB_COLOR_KEY_VID_SRC:
val = 3;
break;
default:
return -EINVAL;
}
enable_lcd_clocks(1);
MOD_REG_FLD(DISPC_CONFIG, FLD_MASK(shift, 2), val << shift);
if (val != 0)
dispc_write_reg(tr_reg, ck->trans_key);
dispc_write_reg(df_reg, ck->background);
enable_lcd_clocks(0);
dispc.color_key = *ck;
return 0;
}
static int omap_dispc_get_color_key(struct omapfb_color_key *ck)
{
*ck = dispc.color_key;
return 0;
}
static void load_palette(void)
{
}
static int omap_dispc_set_update_mode(enum omapfb_update_mode mode)
{
int r = 0;
if (mode != dispc.update_mode) {
switch (mode) {
case OMAPFB_AUTO_UPDATE:
case OMAPFB_MANUAL_UPDATE:
enable_lcd_clocks(1);
omap_dispc_enable_lcd_out(1);
dispc.update_mode = mode;
break;
case OMAPFB_UPDATE_DISABLED:
init_completion(&dispc.frame_done);
omap_dispc_enable_lcd_out(0);
if (!wait_for_completion_timeout(&dispc.frame_done,
msecs_to_jiffies(500))) {
dev_err(dispc.fbdev->dev,
"timeout waiting for FRAME DONE\n");
}
dispc.update_mode = mode;
enable_lcd_clocks(0);
break;
default:
r = -EINVAL;
}
}
return r;
}
static void omap_dispc_get_caps(int plane, struct omapfb_caps *caps)
{
caps->ctrl |= OMAPFB_CAPS_PLANE_RELOCATE_MEM;
if (plane > 0)
caps->ctrl |= OMAPFB_CAPS_PLANE_SCALE;
caps->plane_color |= (1 << OMAPFB_COLOR_RGB565) |
(1 << OMAPFB_COLOR_YUV422) |
(1 << OMAPFB_COLOR_YUY422);
if (plane == 0)
caps->plane_color |= (1 << OMAPFB_COLOR_CLUT_8BPP) |
(1 << OMAPFB_COLOR_CLUT_4BPP) |
(1 << OMAPFB_COLOR_CLUT_2BPP) |
(1 << OMAPFB_COLOR_CLUT_1BPP) |
(1 << OMAPFB_COLOR_RGB444);
}
static enum omapfb_update_mode omap_dispc_get_update_mode(void)
{
return dispc.update_mode;
}
static void setup_color_conv_coef(void)
{
u32 mask = FLD_MASK(16, 11) | FLD_MASK(0, 11);
int cf1_reg = DISPC_VID1_BASE + DISPC_VID_CONV_COEF0;
int cf2_reg = DISPC_VID2_BASE + DISPC_VID_CONV_COEF0;
int at1_reg = DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES;
int at2_reg = DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES;
const struct color_conv_coef {
int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb;
int full_range;
} ctbl_bt601_5 = {
298, 409, 0, 298, -208, -100, 298, 0, 517, 0,
};
const struct color_conv_coef *ct;
#define CVAL(x, y) (((x & 2047) << 16) | (y & 2047))
ct = &ctbl_bt601_5;
MOD_REG_FLD(cf1_reg, mask, CVAL(ct->rcr, ct->ry));
MOD_REG_FLD(cf1_reg + 4, mask, CVAL(ct->gy, ct->rcb));
MOD_REG_FLD(cf1_reg + 8, mask, CVAL(ct->gcb, ct->gcr));
MOD_REG_FLD(cf1_reg + 12, mask, CVAL(ct->bcr, ct->by));
MOD_REG_FLD(cf1_reg + 16, mask, CVAL(0, ct->bcb));
MOD_REG_FLD(cf2_reg, mask, CVAL(ct->rcr, ct->ry));
MOD_REG_FLD(cf2_reg + 4, mask, CVAL(ct->gy, ct->rcb));
MOD_REG_FLD(cf2_reg + 8, mask, CVAL(ct->gcb, ct->gcr));
MOD_REG_FLD(cf2_reg + 12, mask, CVAL(ct->bcr, ct->by));
MOD_REG_FLD(cf2_reg + 16, mask, CVAL(0, ct->bcb));
#undef CVAL
MOD_REG_FLD(at1_reg, (1 << 11), ct->full_range);
MOD_REG_FLD(at2_reg, (1 << 11), ct->full_range);
}
static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div)
{
unsigned long fck, lck;
*lck_div = 1;
pck = max(1, pck);
fck = clk_get_rate(dispc.dss1_fck);
lck = fck;
*pck_div = (lck + pck - 1) / pck;
if (is_tft)
*pck_div = max(2, *pck_div);
else
*pck_div = max(3, *pck_div);
if (*pck_div > 255) {
*pck_div = 255;
lck = pck * *pck_div;
*lck_div = fck / lck;
BUG_ON(*lck_div < 1);
if (*lck_div > 255) {
*lck_div = 255;
dev_warn(dispc.fbdev->dev, "pixclock %d kHz too low.\n",
pck / 1000);
}
}
}
static void set_lcd_tft_mode(int enable)
{
u32 mask;
mask = 1 << 3;
MOD_REG_FLD(DISPC_CONTROL, mask, enable ? mask : 0);
}
static void set_lcd_timings(void)
{
u32 l;
int lck_div, pck_div;
struct lcd_panel *panel = dispc.fbdev->panel;
int is_tft = panel->config & OMAP_LCDC_PANEL_TFT;
unsigned long fck;
l = dispc_read_reg(DISPC_TIMING_H);
l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8));
l |= ( max(1, (min(64, panel->hsw))) - 1 ) << 0;
l |= ( max(1, (min(256, panel->hfp))) - 1 ) << 8;
l |= ( max(1, (min(256, panel->hbp))) - 1 ) << 20;
dispc_write_reg(DISPC_TIMING_H, l);
l = dispc_read_reg(DISPC_TIMING_V);
l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8));
l |= ( max(1, (min(64, panel->vsw))) - 1 ) << 0;
l |= ( max(0, (min(255, panel->vfp))) - 0 ) << 8;
l |= ( max(0, (min(255, panel->vbp))) - 0 ) << 20;
dispc_write_reg(DISPC_TIMING_V, l);
l = dispc_read_reg(DISPC_POL_FREQ);
l &= ~FLD_MASK(12, 6);
l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 12;
l |= panel->acb & 0xff;
dispc_write_reg(DISPC_POL_FREQ, l);
calc_ck_div(is_tft, panel->pixel_clock * 1000, &lck_div, &pck_div);
l = dispc_read_reg(DISPC_DIVISOR);
l &= ~(FLD_MASK(16, 8) | FLD_MASK(0, 8));
l |= (lck_div << 16) | (pck_div << 0);
dispc_write_reg(DISPC_DIVISOR, l);
/* update panel info with the exact clock */
fck = clk_get_rate(dispc.dss1_fck);
panel->pixel_clock = fck / lck_div / pck_div / 1000;
}
static void recalc_irq_mask(void)
{
int i;
unsigned long irq_mask = DISPC_IRQ_MASK_ERROR;
for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
if (!dispc.irq_handlers[i].callback)
continue;
irq_mask |= dispc.irq_handlers[i].irq_mask;
}
enable_lcd_clocks(1);
MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask);
enable_lcd_clocks(0);
}
int omap_dispc_request_irq(unsigned long irq_mask, void (*callback)(void *data),
void *data)
{
int i;
BUG_ON(callback == NULL);
for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
if (dispc.irq_handlers[i].callback)
continue;
dispc.irq_handlers[i].irq_mask = irq_mask;
dispc.irq_handlers[i].callback = callback;
dispc.irq_handlers[i].data = data;
recalc_irq_mask();
return 0;
}
return -EBUSY;
}
EXPORT_SYMBOL(omap_dispc_request_irq);
void omap_dispc_free_irq(unsigned long irq_mask, void (*callback)(void *data),
void *data)
{
int i;
for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
if (dispc.irq_handlers[i].callback == callback &&
dispc.irq_handlers[i].data == data) {
dispc.irq_handlers[i].irq_mask = 0;
dispc.irq_handlers[i].callback = NULL;
dispc.irq_handlers[i].data = NULL;
recalc_irq_mask();
return;
}
}
BUG();
}
EXPORT_SYMBOL(omap_dispc_free_irq);
static irqreturn_t omap_dispc_irq_handler(int irq, void *dev)
{
u32 stat;
int i = 0;
enable_lcd_clocks(1);
stat = dispc_read_reg(DISPC_IRQSTATUS);
if (stat & DISPC_IRQ_FRAMEMASK)
complete(&dispc.frame_done);
if (stat & DISPC_IRQ_MASK_ERROR) {
if (printk_ratelimit()) {
dev_err(dispc.fbdev->dev, "irq error status %04x\n",
stat & 0x7fff);
}
}
for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
if (unlikely(dispc.irq_handlers[i].callback &&
(stat & dispc.irq_handlers[i].irq_mask)))
dispc.irq_handlers[i].callback(
dispc.irq_handlers[i].data);
}
dispc_write_reg(DISPC_IRQSTATUS, stat);
enable_lcd_clocks(0);
return IRQ_HANDLED;
}
static int get_dss_clocks(void)
{
dispc.dss_ick = clk_get(&dispc.fbdev->dssdev->dev, "ick");
if (IS_ERR(dispc.dss_ick)) {
dev_err(dispc.fbdev->dev, "can't get ick\n");
return PTR_ERR(dispc.dss_ick);
}
dispc.dss1_fck = clk_get(&dispc.fbdev->dssdev->dev, "fck");
if (IS_ERR(dispc.dss1_fck)) {
dev_err(dispc.fbdev->dev, "can't get dss1_fck\n");
clk_put(dispc.dss_ick);
return PTR_ERR(dispc.dss1_fck);
}
dispc.dss_54m_fck = clk_get(&dispc.fbdev->dssdev->dev, "tv_clk");
if (IS_ERR(dispc.dss_54m_fck)) {
dev_err(dispc.fbdev->dev, "can't get tv_fck\n");
clk_put(dispc.dss_ick);
clk_put(dispc.dss1_fck);
return PTR_ERR(dispc.dss_54m_fck);
}
return 0;
}
static void put_dss_clocks(void)
{
clk_put(dispc.dss_54m_fck);
clk_put(dispc.dss1_fck);
clk_put(dispc.dss_ick);
}
static void enable_lcd_clocks(int enable)
{
if (enable) {
clk_enable(dispc.dss_ick);
clk_enable(dispc.dss1_fck);
} else {
clk_disable(dispc.dss1_fck);
clk_disable(dispc.dss_ick);
}
}
static void enable_digit_clocks(int enable)
{
if (enable)
clk_enable(dispc.dss_54m_fck);
else
clk_disable(dispc.dss_54m_fck);
}
static void omap_dispc_suspend(void)
{
if (dispc.update_mode == OMAPFB_AUTO_UPDATE) {
init_completion(&dispc.frame_done);
omap_dispc_enable_lcd_out(0);
if (!wait_for_completion_timeout(&dispc.frame_done,
msecs_to_jiffies(500))) {
dev_err(dispc.fbdev->dev,
"timeout waiting for FRAME DONE\n");
}
enable_lcd_clocks(0);
}
}
static void omap_dispc_resume(void)
{
if (dispc.update_mode == OMAPFB_AUTO_UPDATE) {
enable_lcd_clocks(1);
if (!dispc.ext_mode) {
set_lcd_timings();
load_palette();
}
omap_dispc_enable_lcd_out(1);
}
}
static int omap_dispc_update_window(struct fb_info *fbi,
struct omapfb_update_window *win,
void (*complete_callback)(void *arg),
void *complete_callback_data)
{
return dispc.update_mode == OMAPFB_UPDATE_DISABLED ? -ENODEV : 0;
}
static int mmap_kern(struct omapfb_mem_region *region)
{
struct vm_struct *kvma;
struct vm_area_struct vma;
pgprot_t pgprot;
unsigned long vaddr;
kvma = get_vm_area(region->size, VM_IOREMAP);
if (kvma == NULL) {
dev_err(dispc.fbdev->dev, "can't get kernel vm area\n");
return -ENOMEM;
}
vma.vm_mm = &init_mm;
vaddr = (unsigned long)kvma->addr;
pgprot = pgprot_writecombine(pgprot_kernel);
vma.vm_start = vaddr;
vma.vm_end = vaddr + region->size;
if (io_remap_pfn_range(&vma, vaddr, region->paddr >> PAGE_SHIFT,
region->size, pgprot) < 0) {
dev_err(dispc.fbdev->dev, "kernel mmap for FBMEM failed\n");
return -EAGAIN;
}
region->vaddr = (void *)vaddr;
return 0;
}
static void mmap_user_open(struct vm_area_struct *vma)
{
int plane = (int)vma->vm_private_data;
atomic_inc(&dispc.map_count[plane]);
}
static void mmap_user_close(struct vm_area_struct *vma)
{
int plane = (int)vma->vm_private_data;
atomic_dec(&dispc.map_count[plane]);
}
static const struct vm_operations_struct mmap_user_ops = {
.open = mmap_user_open,
.close = mmap_user_close,
};
static int omap_dispc_mmap_user(struct fb_info *info,
struct vm_area_struct *vma)
{
struct omapfb_plane_struct *plane = info->par;
unsigned long off;
unsigned long start;
u32 len;
if (vma->vm_end - vma->vm_start == 0)
return 0;
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
off = vma->vm_pgoff << PAGE_SHIFT;
start = info->fix.smem_start;
len = info->fix.smem_len;
if (off >= len)
return -EINVAL;
if ((vma->vm_end - vma->vm_start + off) > len)
return -EINVAL;
off += start;
vma->vm_pgoff = off >> PAGE_SHIFT;
vma->vm_flags |= VM_IO | VM_RESERVED;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_ops = &mmap_user_ops;
vma->vm_private_data = (void *)plane->idx;
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
/* vm_ops.open won't be called for mmap itself. */
atomic_inc(&dispc.map_count[plane->idx]);
return 0;
}
static void unmap_kern(struct omapfb_mem_region *region)
{
vunmap(region->vaddr);
}
static int alloc_palette_ram(void)
{
dispc.palette_vaddr = dma_alloc_writecombine(dispc.fbdev->dev,
MAX_PALETTE_SIZE, &dispc.palette_paddr, GFP_KERNEL);
if (dispc.palette_vaddr == NULL) {
dev_err(dispc.fbdev->dev, "failed to alloc palette memory\n");
return -ENOMEM;
}
return 0;
}
static void free_palette_ram(void)
{
dma_free_writecombine(dispc.fbdev->dev, MAX_PALETTE_SIZE,
dispc.palette_vaddr, dispc.palette_paddr);
}
static int alloc_fbmem(struct omapfb_mem_region *region)
{
region->vaddr = dma_alloc_writecombine(dispc.fbdev->dev,
region->size, &region->paddr, GFP_KERNEL);
if (region->vaddr == NULL) {
dev_err(dispc.fbdev->dev, "unable to allocate FB DMA memory\n");
return -ENOMEM;
}
return 0;
}
static void free_fbmem(struct omapfb_mem_region *region)
{
dma_free_writecombine(dispc.fbdev->dev, region->size,
region->vaddr, region->paddr);
}
static struct resmap *init_resmap(unsigned long start, size_t size)
{
unsigned page_cnt;
struct resmap *res_map;
page_cnt = PAGE_ALIGN(size) / PAGE_SIZE;
res_map =
kzalloc(sizeof(struct resmap) + RESMAP_SIZE(page_cnt), GFP_KERNEL);
if (res_map == NULL)
return NULL;
res_map->start = start;
res_map->page_cnt = page_cnt;
res_map->map = (unsigned long *)(res_map + 1);
return res_map;
}
static void cleanup_resmap(struct resmap *res_map)
{
kfree(res_map);
}
static inline int resmap_mem_type(unsigned long start)
{
if (start >= OMAP2_SRAM_START &&
start < OMAP2_SRAM_START + OMAP2_SRAM_SIZE)
return OMAPFB_MEMTYPE_SRAM;
else
return OMAPFB_MEMTYPE_SDRAM;
}
static inline int resmap_page_reserved(struct resmap *res_map, unsigned page_nr)
{
return *RESMAP_PTR(res_map, page_nr) & RESMAP_MASK(page_nr) ? 1 : 0;
}
static inline void resmap_reserve_page(struct resmap *res_map, unsigned page_nr)
{
BUG_ON(resmap_page_reserved(res_map, page_nr));
*RESMAP_PTR(res_map, page_nr) |= RESMAP_MASK(page_nr);
}
static inline void resmap_free_page(struct resmap *res_map, unsigned page_nr)
{
BUG_ON(!resmap_page_reserved(res_map, page_nr));
*RESMAP_PTR(res_map, page_nr) &= ~RESMAP_MASK(page_nr);
}
static void resmap_reserve_region(unsigned long start, size_t size)
{
struct resmap *res_map;
unsigned start_page;
unsigned end_page;
int mtype;
unsigned i;
mtype = resmap_mem_type(start);
res_map = dispc.res_map[mtype];
dev_dbg(dispc.fbdev->dev, "reserve mem type %d start %08lx size %d\n",
mtype, start, size);
start_page = (start - res_map->start) / PAGE_SIZE;
end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE;
for (i = start_page; i < end_page; i++)
resmap_reserve_page(res_map, i);
}
static void resmap_free_region(unsigned long start, size_t size)
{
struct resmap *res_map;
unsigned start_page;
unsigned end_page;
unsigned i;
int mtype;
mtype = resmap_mem_type(start);
res_map = dispc.res_map[mtype];
dev_dbg(dispc.fbdev->dev, "free mem type %d start %08lx size %d\n",
mtype, start, size);
start_page = (start - res_map->start) / PAGE_SIZE;
end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE;
for (i = start_page; i < end_page; i++)
resmap_free_page(res_map, i);
}
static unsigned long resmap_alloc_region(int mtype, size_t size)
{
unsigned i;
unsigned total;
unsigned start_page;
unsigned long start;
struct resmap *res_map = dispc.res_map[mtype];
BUG_ON(mtype >= DISPC_MEMTYPE_NUM || res_map == NULL || !size);
size = PAGE_ALIGN(size) / PAGE_SIZE;
start_page = 0;
total = 0;
for (i = 0; i < res_map->page_cnt; i++) {
if (resmap_page_reserved(res_map, i)) {
start_page = i + 1;
total = 0;
} else if (++total == size)
break;
}
if (total < size)
return 0;
start = res_map->start + start_page * PAGE_SIZE;
resmap_reserve_region(start, size * PAGE_SIZE);
return start;
}
/* Note that this will only work for user mappings, we don't deal with
* kernel mappings here, so fbcon will keep using the old region.
*/
static int omap_dispc_setup_mem(int plane, size_t size, int mem_type,
unsigned long *paddr)
{
struct omapfb_mem_region *rg;
unsigned long new_addr = 0;
if ((unsigned)plane > dispc.mem_desc.region_cnt)
return -EINVAL;
if (mem_type >= DISPC_MEMTYPE_NUM)
return -EINVAL;
if (dispc.res_map[mem_type] == NULL)
return -ENOMEM;
rg = &dispc.mem_desc.region[plane];
if (size == rg->size && mem_type == rg->type)
return 0;
if (atomic_read(&dispc.map_count[plane]))
return -EBUSY;
if (rg->size != 0)
resmap_free_region(rg->paddr, rg->size);
if (size != 0) {
new_addr = resmap_alloc_region(mem_type, size);
if (!new_addr) {
/* Reallocate old region. */
resmap_reserve_region(rg->paddr, rg->size);
return -ENOMEM;
}
}
rg->paddr = new_addr;
rg->size = size;
rg->type = mem_type;
*paddr = new_addr;
return 0;
}
static int setup_fbmem(struct omapfb_mem_desc *req_md)
{
struct omapfb_mem_region *rg;
int i;
int r;
unsigned long mem_start[DISPC_MEMTYPE_NUM];
unsigned long mem_end[DISPC_MEMTYPE_NUM];
if (!req_md->region_cnt) {
dev_err(dispc.fbdev->dev, "no memory regions defined\n");
return -ENOENT;
}
rg = &req_md->region[0];
memset(mem_start, 0xff, sizeof(mem_start));
memset(mem_end, 0, sizeof(mem_end));
for (i = 0; i < req_md->region_cnt; i++, rg++) {
int mtype;
if (rg->paddr) {
rg->alloc = 0;
if (rg->vaddr == NULL) {
rg->map = 1;
if ((r = mmap_kern(rg)) < 0)
return r;
}
} else {
if (rg->type != OMAPFB_MEMTYPE_SDRAM) {
dev_err(dispc.fbdev->dev,
"unsupported memory type\n");
return -EINVAL;
}
rg->alloc = rg->map = 1;
if ((r = alloc_fbmem(rg)) < 0)
return r;
}
mtype = rg->type;
if (rg->paddr < mem_start[mtype])
mem_start[mtype] = rg->paddr;
if (rg->paddr + rg->size > mem_end[mtype])
mem_end[mtype] = rg->paddr + rg->size;
}
for (i = 0; i < DISPC_MEMTYPE_NUM; i++) {
unsigned long start;
size_t size;
if (mem_end[i] == 0)
continue;
start = mem_start[i];
size = mem_end[i] - start;
dispc.res_map[i] = init_resmap(start, size);
r = -ENOMEM;
if (dispc.res_map[i] == NULL)
goto fail;
/* Initial state is that everything is reserved. This
* includes possible holes as well, which will never be
* freed.
*/
resmap_reserve_region(start, size);
}
dispc.mem_desc = *req_md;
return 0;
fail:
for (i = 0; i < DISPC_MEMTYPE_NUM; i++) {
if (dispc.res_map[i] != NULL)
cleanup_resmap(dispc.res_map[i]);
}
return r;
}
static void cleanup_fbmem(void)
{
struct omapfb_mem_region *rg;
int i;
for (i = 0; i < DISPC_MEMTYPE_NUM; i++) {
if (dispc.res_map[i] != NULL)
cleanup_resmap(dispc.res_map[i]);
}
rg = &dispc.mem_desc.region[0];
for (i = 0; i < dispc.mem_desc.region_cnt; i++, rg++) {
if (rg->alloc)
free_fbmem(rg);
else {
if (rg->map)
unmap_kern(rg);
}
}
}
static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
struct omapfb_mem_desc *req_vram)
{
int r;
u32 l;
struct lcd_panel *panel = fbdev->panel;
void __iomem *ram_fw_base;
int tmo = 10000;
int skip_init = 0;
int i;
memset(&dispc, 0, sizeof(dispc));
dispc.base = ioremap(DISPC_BASE, SZ_1K);
if (!dispc.base) {
dev_err(fbdev->dev, "can't ioremap DISPC\n");
return -ENOMEM;
}
dispc.fbdev = fbdev;
dispc.ext_mode = ext_mode;
init_completion(&dispc.frame_done);
if ((r = get_dss_clocks()) < 0)
goto fail0;
enable_lcd_clocks(1);
#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
l = dispc_read_reg(DISPC_CONTROL);
/* LCD enabled ? */
if (l & 1) {
pr_info("omapfb: skipping hardware initialization\n");
skip_init = 1;
}
#endif
if (!skip_init) {
/* Reset monitoring works only w/ the 54M clk */
enable_digit_clocks(1);
/* Soft reset */
MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1);
while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) {
if (!--tmo) {
dev_err(dispc.fbdev->dev, "soft reset failed\n");
r = -ENODEV;
enable_digit_clocks(0);
goto fail1;
}
}
enable_digit_clocks(0);
}
/* Enable smart standby/idle, autoidle and wakeup */
l = dispc_read_reg(DISPC_SYSCONFIG);
l &= ~((3 << 12) | (3 << 3));
l |= (2 << 12) | (2 << 3) | (1 << 2) | (1 << 0);
dispc_write_reg(DISPC_SYSCONFIG, l);
omap_writel(1 << 0, DSS_BASE + DSS_SYSCONFIG);
/* Set functional clock autogating */
l = dispc_read_reg(DISPC_CONFIG);
l |= 1 << 9;
dispc_write_reg(DISPC_CONFIG, l);
l = dispc_read_reg(DISPC_IRQSTATUS);
dispc_write_reg(DISPC_IRQSTATUS, l);
recalc_irq_mask();
if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler,
0, MODULE_NAME, fbdev)) < 0) {
dev_err(dispc.fbdev->dev, "can't get DSS IRQ\n");
goto fail1;
}
/* L3 firewall setting: enable access to OCM RAM */
ram_fw_base = ioremap(0x68005000, SZ_1K);
if (!ram_fw_base) {
dev_err(dispc.fbdev->dev, "Cannot ioremap to enable OCM RAM\n");
goto fail1;
}
__raw_writel(0x402000b0, ram_fw_base + 0xa0);
iounmap(ram_fw_base);
if ((r = alloc_palette_ram()) < 0)
goto fail2;
if ((r = setup_fbmem(req_vram)) < 0)
goto fail3;
if (!skip_init) {
for (i = 0; i < dispc.mem_desc.region_cnt; i++) {
memset(dispc.mem_desc.region[i].vaddr, 0,
dispc.mem_desc.region[i].size);
}
/* Set logic clock to fck, pixel clock to fck/2 for now */
MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1 << 16);
MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(0, 8), 2 << 0);
setup_plane_fifo(0, ext_mode);
setup_plane_fifo(1, ext_mode);
setup_plane_fifo(2, ext_mode);
setup_color_conv_coef();
set_lcd_tft_mode(panel->config & OMAP_LCDC_PANEL_TFT);
set_load_mode(DISPC_LOAD_FRAME_ONLY);
if (!ext_mode) {
set_lcd_data_lines(panel->data_lines);
omap_dispc_set_lcd_size(panel->x_res, panel->y_res);
set_lcd_timings();
} else
set_lcd_data_lines(panel->bpp);
enable_rfbi_mode(ext_mode);
}
l = dispc_read_reg(DISPC_REVISION);
pr_info("omapfb: DISPC version %d.%d initialized\n",
l >> 4 & 0x0f, l & 0x0f);
enable_lcd_clocks(0);
return 0;
fail3:
free_palette_ram();
fail2:
free_irq(INT_24XX_DSS_IRQ, fbdev);
fail1:
enable_lcd_clocks(0);
put_dss_clocks();
fail0:
iounmap(dispc.base);
return r;
}
static void omap_dispc_cleanup(void)
{
int i;
omap_dispc_set_update_mode(OMAPFB_UPDATE_DISABLED);
/* This will also disable clocks that are on */
for (i = 0; i < dispc.mem_desc.region_cnt; i++)
omap_dispc_enable_plane(i, 0);
cleanup_fbmem();
free_palette_ram();
free_irq(INT_24XX_DSS_IRQ, dispc.fbdev);
put_dss_clocks();
iounmap(dispc.base);
}
const struct lcd_ctrl omap2_int_ctrl = {
.name = "internal",
.init = omap_dispc_init,
.cleanup = omap_dispc_cleanup,
.get_caps = omap_dispc_get_caps,
.set_update_mode = omap_dispc_set_update_mode,
.get_update_mode = omap_dispc_get_update_mode,
.update_window = omap_dispc_update_window,
.suspend = omap_dispc_suspend,
.resume = omap_dispc_resume,
.setup_plane = omap_dispc_setup_plane,
.setup_mem = omap_dispc_setup_mem,
.set_scale = omap_dispc_set_scale,
.enable_plane = omap_dispc_enable_plane,
.set_color_key = omap_dispc_set_color_key,
.get_color_key = omap_dispc_get_color_key,
.mmap = omap_dispc_mmap_user,
};
#ifndef _DISPC_H
#define _DISPC_H
#include <linux/interrupt.h>
#define DISPC_PLANE_GFX 0
#define DISPC_PLANE_VID1 1
#define DISPC_PLANE_VID2 2
#define DISPC_RGB_1_BPP 0x00
#define DISPC_RGB_2_BPP 0x01
#define DISPC_RGB_4_BPP 0x02
#define DISPC_RGB_8_BPP 0x03
#define DISPC_RGB_12_BPP 0x04
#define DISPC_RGB_16_BPP 0x06
#define DISPC_RGB_24_BPP 0x08
#define DISPC_RGB_24_BPP_UNPACK_32 0x09
#define DISPC_YUV2_422 0x0a
#define DISPC_UYVY_422 0x0b
#define DISPC_BURST_4x32 0
#define DISPC_BURST_8x32 1
#define DISPC_BURST_16x32 2
#define DISPC_LOAD_CLUT_AND_FRAME 0x00
#define DISPC_LOAD_CLUT_ONLY 0x01
#define DISPC_LOAD_FRAME_ONLY 0x02
#define DISPC_LOAD_CLUT_ONCE_FRAME 0x03
#define DISPC_TFT_DATA_LINES_12 0
#define DISPC_TFT_DATA_LINES_16 1
#define DISPC_TFT_DATA_LINES_18 2
#define DISPC_TFT_DATA_LINES_24 3
extern void omap_dispc_set_lcd_size(int width, int height);
extern void omap_dispc_enable_lcd_out(int enable);
extern void omap_dispc_enable_digit_out(int enable);
extern int omap_dispc_request_irq(unsigned long irq_mask,
void (*callback)(void *data), void *data);
extern void omap_dispc_free_irq(unsigned long irq_mask,
void (*callback)(void *data), void *data);
extern const struct lcd_ctrl omap2_int_ctrl;
#endif
...@@ -207,11 +207,7 @@ struct omapfb_device { ...@@ -207,11 +207,7 @@ struct omapfb_device {
struct platform_device *dssdev; /* dummy dev for clocks */ struct platform_device *dssdev; /* dummy dev for clocks */
}; };
#ifdef CONFIG_ARCH_OMAP1
extern struct lcd_ctrl omap1_lcd_ctrl; extern struct lcd_ctrl omap1_lcd_ctrl;
#else
extern struct lcd_ctrl omap2_disp_ctrl;
#endif
extern void omapfb_register_panel(struct lcd_panel *panel); extern void omapfb_register_panel(struct lcd_panel *panel);
extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval); extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval);
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include "omapfb.h" #include "omapfb.h"
#include "lcdc.h" #include "lcdc.h"
#include "dispc.h"
#define MODULE_NAME "omapfb" #define MODULE_NAME "omapfb"
...@@ -106,11 +105,7 @@ static struct platform_device omapdss_device = { ...@@ -106,11 +105,7 @@ static struct platform_device omapdss_device = {
extern struct lcd_ctrl hwa742_ctrl; extern struct lcd_ctrl hwa742_ctrl;
static const struct lcd_ctrl *ctrls[] = { static const struct lcd_ctrl *ctrls[] = {
#ifdef CONFIG_ARCH_OMAP1
&omap1_int_ctrl, &omap1_int_ctrl,
#else
&omap2_int_ctrl,
#endif
#ifdef CONFIG_FB_OMAP_LCDC_HWA742 #ifdef CONFIG_FB_OMAP_LCDC_HWA742
&hwa742_ctrl, &hwa742_ctrl,
...@@ -118,11 +113,7 @@ static const struct lcd_ctrl *ctrls[] = { ...@@ -118,11 +113,7 @@ static const struct lcd_ctrl *ctrls[] = {
}; };
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
#ifdef CONFIG_ARCH_OMAP1
extern struct lcd_ctrl_extif omap1_ext_if; extern struct lcd_ctrl_extif omap1_ext_if;
#else
extern struct lcd_ctrl_extif omap2_ext_if;
#endif
#endif #endif
static void omapfb_rqueue_lock(struct omapfb_device *fbdev) static void omapfb_rqueue_lock(struct omapfb_device *fbdev)
...@@ -1717,16 +1708,9 @@ static int omapfb_do_probe(struct platform_device *pdev, ...@@ -1717,16 +1708,9 @@ static int omapfb_do_probe(struct platform_device *pdev,
mutex_init(&fbdev->rqueue_mutex); mutex_init(&fbdev->rqueue_mutex);
#ifdef CONFIG_ARCH_OMAP1
fbdev->int_ctrl = &omap1_int_ctrl; fbdev->int_ctrl = &omap1_int_ctrl;
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
fbdev->ext_if = &omap1_ext_if; fbdev->ext_if = &omap1_ext_if;
#endif
#else /* OMAP2 */
fbdev->int_ctrl = &omap2_int_ctrl;
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
fbdev->ext_if = &omap2_ext_if;
#endif
#endif #endif
if (omapfb_find_ctrl(fbdev) < 0) { if (omapfb_find_ctrl(fbdev) < 0) {
dev_err(fbdev->dev, dev_err(fbdev->dev,
......
/*
* OMAP2 Remote Frame Buffer Interface support
*
* Copyright (C) 2005 Nokia Corporation
* Author: Juha Yrjölä <juha.yrjola@nokia.com>
* Imre Deak <imre.deak@nokia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include "omapfb.h"
#include "dispc.h"
/* To work around an RFBI transfer rate limitation */
#define OMAP_RFBI_RATE_LIMIT 1
#define RFBI_BASE 0x48050800
#define RFBI_REVISION 0x0000
#define RFBI_SYSCONFIG 0x0010
#define RFBI_SYSSTATUS 0x0014
#define RFBI_CONTROL 0x0040
#define RFBI_PIXEL_CNT 0x0044
#define RFBI_LINE_NUMBER 0x0048
#define RFBI_CMD 0x004c
#define RFBI_PARAM 0x0050
#define RFBI_DATA 0x0054
#define RFBI_READ 0x0058
#define RFBI_STATUS 0x005c
#define RFBI_CONFIG0 0x0060
#define RFBI_ONOFF_TIME0 0x0064
#define RFBI_CYCLE_TIME0 0x0068
#define RFBI_DATA_CYCLE1_0 0x006c
#define RFBI_DATA_CYCLE2_0 0x0070
#define RFBI_DATA_CYCLE3_0 0x0074
#define RFBI_VSYNC_WIDTH 0x0090
#define RFBI_HSYNC_WIDTH 0x0094
#define DISPC_BASE 0x48050400
#define DISPC_CONTROL 0x0040
#define DISPC_IRQ_FRAMEMASK 0x0001
static struct {
void __iomem *base;
void (*lcdc_callback)(void *data);
void *lcdc_callback_data;
unsigned long l4_khz;
int bits_per_cycle;
struct omapfb_device *fbdev;
struct clk *dss_ick;
struct clk *dss1_fck;
unsigned tearsync_pin_cnt;
unsigned tearsync_mode;
} rfbi;
static inline void rfbi_write_reg(int idx, u32 val)
{
__raw_writel(val, rfbi.base + idx);
}
static inline u32 rfbi_read_reg(int idx)
{
return __raw_readl(rfbi.base + idx);
}
static int rfbi_get_clocks(void)
{
rfbi.dss_ick = clk_get(&rfbi.fbdev->dssdev->dev, "ick");
if (IS_ERR(rfbi.dss_ick)) {
dev_err(rfbi.fbdev->dev, "can't get ick\n");
return PTR_ERR(rfbi.dss_ick);
}
rfbi.dss1_fck = clk_get(&rfbi.fbdev->dssdev->dev, "fck");
if (IS_ERR(rfbi.dss1_fck)) {
dev_err(rfbi.fbdev->dev, "can't get dss1_fck\n");
clk_put(rfbi.dss_ick);
return PTR_ERR(rfbi.dss1_fck);
}
return 0;
}
static void rfbi_put_clocks(void)
{
clk_put(rfbi.dss1_fck);
clk_put(rfbi.dss_ick);
}
static void rfbi_enable_clocks(int enable)
{
if (enable) {
clk_enable(rfbi.dss_ick);
clk_enable(rfbi.dss1_fck);
} else {
clk_disable(rfbi.dss1_fck);
clk_disable(rfbi.dss_ick);
}
}
#ifdef VERBOSE
static void rfbi_print_timings(void)
{
u32 l;
u32 time;
l = rfbi_read_reg(RFBI_CONFIG0);
time = 1000000000 / rfbi.l4_khz;
if (l & (1 << 4))
time *= 2;
dev_dbg(rfbi.fbdev->dev, "Tick time %u ps\n", time);
l = rfbi_read_reg(RFBI_ONOFF_TIME0);
dev_dbg(rfbi.fbdev->dev,
"CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, "
"REONTIME %d, REOFFTIME %d\n",
l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f,
(l >> 20) & 0x0f, (l >> 24) & 0x3f);
l = rfbi_read_reg(RFBI_CYCLE_TIME0);
dev_dbg(rfbi.fbdev->dev,
"WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, "
"ACCESSTIME %d\n",
(l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f,
(l >> 22) & 0x3f);
}
#else
static void rfbi_print_timings(void) {}
#endif
static void rfbi_set_timings(const struct extif_timings *t)
{
u32 l;
BUG_ON(!t->converted);
rfbi_enable_clocks(1);
rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]);
rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]);
l = rfbi_read_reg(RFBI_CONFIG0);
l &= ~(1 << 4);
l |= (t->tim[2] ? 1 : 0) << 4;
rfbi_write_reg(RFBI_CONFIG0, l);
rfbi_print_timings();
rfbi_enable_clocks(0);
}
static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
{
*clk_period = 1000000000 / rfbi.l4_khz;
*max_clk_div = 2;
}
static int ps_to_rfbi_ticks(int time, int div)
{
unsigned long tick_ps;
int ret;
/* Calculate in picosecs to yield more exact results */
tick_ps = 1000000000 / (rfbi.l4_khz) * div;
ret = (time + tick_ps - 1) / tick_ps;
return ret;
}
#ifdef OMAP_RFBI_RATE_LIMIT
static unsigned long rfbi_get_max_tx_rate(void)
{
unsigned long l4_rate, dss1_rate;
int min_l4_ticks = 0;
int i;
/* According to TI this can't be calculated so make the
* adjustments for a couple of known frequencies and warn for
* others.
*/
static const struct {
unsigned long l4_clk; /* HZ */
unsigned long dss1_clk; /* HZ */
unsigned long min_l4_ticks;
} ftab[] = {
{ 55, 132, 7, }, /* 7.86 MPix/s */
{ 110, 110, 12, }, /* 9.16 MPix/s */
{ 110, 132, 10, }, /* 11 Mpix/s */
{ 120, 120, 10, }, /* 12 Mpix/s */
{ 133, 133, 10, }, /* 13.3 Mpix/s */
};
l4_rate = rfbi.l4_khz / 1000;
dss1_rate = clk_get_rate(rfbi.dss1_fck) / 1000000;
for (i = 0; i < ARRAY_SIZE(ftab); i++) {
/* Use a window instead of an exact match, to account
* for different DPLL multiplier / divider pairs.
*/
if (abs(ftab[i].l4_clk - l4_rate) < 3 &&
abs(ftab[i].dss1_clk - dss1_rate) < 3) {
min_l4_ticks = ftab[i].min_l4_ticks;
break;
}
}
if (i == ARRAY_SIZE(ftab)) {
/* Can't be sure, return anyway the maximum not
* rate-limited. This might cause a problem only for the
* tearing synchronisation.
*/
dev_err(rfbi.fbdev->dev,
"can't determine maximum RFBI transfer rate\n");
return rfbi.l4_khz * 1000;
}
return rfbi.l4_khz * 1000 / min_l4_ticks;
}
#else
static int rfbi_get_max_tx_rate(void)
{
return rfbi.l4_khz * 1000;
}
#endif
static int rfbi_convert_timings(struct extif_timings *t)
{
u32 l;
int reon, reoff, weon, weoff, cson, csoff, cs_pulse;
int actim, recyc, wecyc;
int div = t->clk_div;
if (div <= 0 || div > 2)
return -1;
/* Make sure that after conversion it still holds that:
* weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff,
* csoff > cson, csoff >= max(weoff, reoff), actim > reon
*/
weon = ps_to_rfbi_ticks(t->we_on_time, div);
weoff = ps_to_rfbi_ticks(t->we_off_time, div);
if (weoff <= weon)
weoff = weon + 1;
if (weon > 0x0f)
return -1;
if (weoff > 0x3f)
return -1;
reon = ps_to_rfbi_ticks(t->re_on_time, div);
reoff = ps_to_rfbi_ticks(t->re_off_time, div);
if (reoff <= reon)
reoff = reon + 1;
if (reon > 0x0f)
return -1;
if (reoff > 0x3f)
return -1;
cson = ps_to_rfbi_ticks(t->cs_on_time, div);
csoff = ps_to_rfbi_ticks(t->cs_off_time, div);
if (csoff <= cson)
csoff = cson + 1;
if (csoff < max(weoff, reoff))
csoff = max(weoff, reoff);
if (cson > 0x0f)
return -1;
if (csoff > 0x3f)
return -1;
l = cson;
l |= csoff << 4;
l |= weon << 10;
l |= weoff << 14;
l |= reon << 20;
l |= reoff << 24;
t->tim[0] = l;
actim = ps_to_rfbi_ticks(t->access_time, div);
if (actim <= reon)
actim = reon + 1;
if (actim > 0x3f)
return -1;
wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div);
if (wecyc < weoff)
wecyc = weoff;
if (wecyc > 0x3f)
return -1;
recyc = ps_to_rfbi_ticks(t->re_cycle_time, div);
if (recyc < reoff)
recyc = reoff;
if (recyc > 0x3f)
return -1;
cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div);
if (cs_pulse > 0x3f)
return -1;
l = wecyc;
l |= recyc << 6;
l |= cs_pulse << 12;
l |= actim << 22;
t->tim[1] = l;
t->tim[2] = div - 1;
t->converted = 1;
return 0;
}
static int rfbi_setup_tearsync(unsigned pin_cnt,
unsigned hs_pulse_time, unsigned vs_pulse_time,
int hs_pol_inv, int vs_pol_inv, int extif_div)
{
int hs, vs;
int min;
u32 l;
if (pin_cnt != 1 && pin_cnt != 2)
return -EINVAL;
hs = ps_to_rfbi_ticks(hs_pulse_time, 1);
vs = ps_to_rfbi_ticks(vs_pulse_time, 1);
if (hs < 2)
return -EDOM;
if (pin_cnt == 2)
min = 2;
else
min = 4;
if (vs < min)
return -EDOM;
if (vs == hs)
return -EINVAL;
rfbi.tearsync_pin_cnt = pin_cnt;
dev_dbg(rfbi.fbdev->dev,
"setup_tearsync: pins %d hs %d vs %d hs_inv %d vs_inv %d\n",
pin_cnt, hs, vs, hs_pol_inv, vs_pol_inv);
rfbi_enable_clocks(1);
rfbi_write_reg(RFBI_HSYNC_WIDTH, hs);
rfbi_write_reg(RFBI_VSYNC_WIDTH, vs);
l = rfbi_read_reg(RFBI_CONFIG0);
if (hs_pol_inv)
l &= ~(1 << 21);
else
l |= 1 << 21;
if (vs_pol_inv)
l &= ~(1 << 20);
else
l |= 1 << 20;
rfbi_enable_clocks(0);
return 0;
}
static int rfbi_enable_tearsync(int enable, unsigned line)
{
u32 l;
dev_dbg(rfbi.fbdev->dev, "tearsync %d line %d mode %d\n",
enable, line, rfbi.tearsync_mode);
if (line > (1 << 11) - 1)
return -EINVAL;
rfbi_enable_clocks(1);
l = rfbi_read_reg(RFBI_CONFIG0);
l &= ~(0x3 << 2);
if (enable) {
rfbi.tearsync_mode = rfbi.tearsync_pin_cnt;
l |= rfbi.tearsync_mode << 2;
} else
rfbi.tearsync_mode = 0;
rfbi_write_reg(RFBI_CONFIG0, l);
rfbi_write_reg(RFBI_LINE_NUMBER, line);
rfbi_enable_clocks(0);
return 0;
}
static void rfbi_write_command(const void *buf, unsigned int len)
{
rfbi_enable_clocks(1);
if (rfbi.bits_per_cycle == 16) {
const u16 *w = buf;
BUG_ON(len & 1);
for (; len; len -= 2)
rfbi_write_reg(RFBI_CMD, *w++);
} else {
const u8 *b = buf;
BUG_ON(rfbi.bits_per_cycle != 8);
for (; len; len--)
rfbi_write_reg(RFBI_CMD, *b++);
}
rfbi_enable_clocks(0);
}
static void rfbi_read_data(void *buf, unsigned int len)
{
rfbi_enable_clocks(1);
if (rfbi.bits_per_cycle == 16) {
u16 *w = buf;
BUG_ON(len & ~1);
for (; len; len -= 2) {
rfbi_write_reg(RFBI_READ, 0);
*w++ = rfbi_read_reg(RFBI_READ);
}
} else {
u8 *b = buf;
BUG_ON(rfbi.bits_per_cycle != 8);
for (; len; len--) {
rfbi_write_reg(RFBI_READ, 0);
*b++ = rfbi_read_reg(RFBI_READ);
}
}
rfbi_enable_clocks(0);
}
static void rfbi_write_data(const void *buf, unsigned int len)
{
rfbi_enable_clocks(1);
if (rfbi.bits_per_cycle == 16) {
const u16 *w = buf;
BUG_ON(len & 1);
for (; len; len -= 2)
rfbi_write_reg(RFBI_PARAM, *w++);
} else {
const u8 *b = buf;
BUG_ON(rfbi.bits_per_cycle != 8);
for (; len; len--)
rfbi_write_reg(RFBI_PARAM, *b++);
}
rfbi_enable_clocks(0);
}
static void rfbi_transfer_area(int width, int height,
void (callback)(void * data), void *data)
{
u32 w;
BUG_ON(callback == NULL);
rfbi_enable_clocks(1);
omap_dispc_set_lcd_size(width, height);
rfbi.lcdc_callback = callback;
rfbi.lcdc_callback_data = data;
rfbi_write_reg(RFBI_PIXEL_CNT, width * height);
w = rfbi_read_reg(RFBI_CONTROL);
w |= 1; /* enable */
if (!rfbi.tearsync_mode)
w |= 1 << 4; /* internal trigger, reset by HW */
rfbi_write_reg(RFBI_CONTROL, w);
omap_dispc_enable_lcd_out(1);
}
static inline void _stop_transfer(void)
{
u32 w;
w = rfbi_read_reg(RFBI_CONTROL);
rfbi_write_reg(RFBI_CONTROL, w & ~(1 << 0));
rfbi_enable_clocks(0);
}
static void rfbi_dma_callback(void *data)
{
_stop_transfer();
rfbi.lcdc_callback(rfbi.lcdc_callback_data);
}
static void rfbi_set_bits_per_cycle(int bpc)
{
u32 l;
rfbi_enable_clocks(1);
l = rfbi_read_reg(RFBI_CONFIG0);
l &= ~(0x03 << 0);
switch (bpc) {
case 8:
break;
case 16:
l |= 3;
break;
default:
BUG();
}
rfbi_write_reg(RFBI_CONFIG0, l);
rfbi.bits_per_cycle = bpc;
rfbi_enable_clocks(0);
}
static int rfbi_init(struct omapfb_device *fbdev)
{
u32 l;
int r;
rfbi.fbdev = fbdev;
rfbi.base = ioremap(RFBI_BASE, SZ_1K);
if (!rfbi.base) {
dev_err(fbdev->dev, "can't ioremap RFBI\n");
return -ENOMEM;
}
if ((r = rfbi_get_clocks()) < 0)
return r;
rfbi_enable_clocks(1);
rfbi.l4_khz = clk_get_rate(rfbi.dss_ick) / 1000;
/* Reset */
rfbi_write_reg(RFBI_SYSCONFIG, 1 << 1);
while (!(rfbi_read_reg(RFBI_SYSSTATUS) & (1 << 0)));
l = rfbi_read_reg(RFBI_SYSCONFIG);
/* Enable autoidle and smart-idle */
l |= (1 << 0) | (2 << 3);
rfbi_write_reg(RFBI_SYSCONFIG, l);
/* 16-bit interface, ITE trigger mode, 16-bit data */
l = (0x03 << 0) | (0x00 << 2) | (0x01 << 5) | (0x02 << 7);
l |= (0 << 9) | (1 << 20) | (1 << 21);
rfbi_write_reg(RFBI_CONFIG0, l);
rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010);
l = rfbi_read_reg(RFBI_CONTROL);
/* Select CS0, clear bypass mode */
l = (0x01 << 2);
rfbi_write_reg(RFBI_CONTROL, l);
r = omap_dispc_request_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback,
NULL);
if (r < 0) {
dev_err(fbdev->dev, "can't get DISPC irq\n");
rfbi_enable_clocks(0);
return r;
}
l = rfbi_read_reg(RFBI_REVISION);
pr_info("omapfb: RFBI version %d.%d initialized\n",
(l >> 4) & 0x0f, l & 0x0f);
rfbi_enable_clocks(0);
return 0;
}
static void rfbi_cleanup(void)
{
omap_dispc_free_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, NULL);
rfbi_put_clocks();
iounmap(rfbi.base);
}
const struct lcd_ctrl_extif omap2_ext_if = {
.init = rfbi_init,
.cleanup = rfbi_cleanup,
.get_clk_info = rfbi_get_clk_info,
.get_max_tx_rate = rfbi_get_max_tx_rate,
.set_bits_per_cycle = rfbi_set_bits_per_cycle,
.convert_timings = rfbi_convert_timings,
.set_timings = rfbi_set_timings,
.write_command = rfbi_write_command,
.read_data = rfbi_read_data,
.write_data = rfbi_write_data,
.transfer_area = rfbi_transfer_area,
.setup_tearsync = rfbi_setup_tearsync,
.enable_tearsync = rfbi_enable_tearsync,
.max_transmit_size = (u32) ~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