mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-11 05:17:10 +09:00
video: tegra: add tegra display controller driver
Notable ommisions: * support for anything but lvds panels * inegration with nvhost driver to sync updates with 3D * FB physical geometry is not set * lacks interface to set overlay/window x,y offset v2 changes: * suspend/resume support * move code into drivers/video/tegra/dc * modularize output support * clean register dumping, add debugfs register file * code review feedback * make the display controller register the framebuffer devices Signed-off-by: Erik Gilling <konkers@android.com>
This commit is contained in:
committed by
Colin Cross
parent
fafa6bbd58
commit
06601cc541
152
arch/arm/mach-tegra/include/mach/dc.h
Normal file
152
arch/arm/mach-tegra/include/mach/dc.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* arch/arm/mach-tegra/include/mach/dc.h
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Erik Gilling <konkers@google.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA_DC_H
|
||||
#define __MACH_TEGRA_DC_H
|
||||
|
||||
|
||||
#define TEGRA_MAX_DC 2
|
||||
#define DC_N_WINDOWS 3
|
||||
|
||||
struct tegra_dc_blend {
|
||||
u32 nokey;
|
||||
u32 one_win;
|
||||
u32 two_win_x;
|
||||
u32 two_win_y;
|
||||
u32 three_win_xy;
|
||||
};
|
||||
|
||||
#define BLEND(key, control, weight0, weight1) \
|
||||
CKEY_ ## key | BLEND_CONTROL_ ## control | \
|
||||
BLEND_WEIGHT0(weight0) | BLEND_WEIGHT0(weight1)
|
||||
|
||||
struct tegra_dc_mode {
|
||||
int pclk;
|
||||
int h_ref_to_sync;
|
||||
int v_ref_to_sync;
|
||||
int h_sync_width;
|
||||
int v_sync_width;
|
||||
int h_back_porch;
|
||||
int v_back_porch;
|
||||
int h_active;
|
||||
int v_active;
|
||||
int h_front_porch;
|
||||
int v_front_porch;
|
||||
};
|
||||
|
||||
enum {
|
||||
TEGRA_DC_OUT_RGB,
|
||||
};
|
||||
|
||||
struct tegra_dc_out {
|
||||
int type;
|
||||
|
||||
unsigned order;
|
||||
unsigned align;
|
||||
|
||||
struct tegra_dc_mode *modes;
|
||||
int n_modes;
|
||||
};
|
||||
|
||||
#define TEGRA_DC_ALIGN_MSB 0
|
||||
#define TEGRA_DC_ALIGN_LSB 1
|
||||
|
||||
#define TEGRA_DC_ORDER_RED_BLUE 0
|
||||
#define TEGRA_DC_ORDER_BLUE_RED 1
|
||||
|
||||
struct tegra_dc;
|
||||
|
||||
struct tegra_dc_win {
|
||||
u8 idx;
|
||||
u8 fmt;
|
||||
u32 flags;
|
||||
|
||||
void *virt_addr;
|
||||
dma_addr_t phys_addr;
|
||||
unsigned x;
|
||||
unsigned y;
|
||||
unsigned w;
|
||||
unsigned h;
|
||||
unsigned out_w;
|
||||
unsigned out_h;
|
||||
|
||||
int dirty;
|
||||
struct tegra_dc *dc;
|
||||
};
|
||||
|
||||
#define TEGRA_WIN_FLAG_ENABLED (1 << 0)
|
||||
#define TEGRA_WIN_FLAG_COLOR_EXPAND (1 << 1)
|
||||
|
||||
/* Note: These are the actual values written to the DC_WIN_COLOR_DEPTH register
|
||||
* and may change in new tegra architectures.
|
||||
*/
|
||||
#define TEGRA_WIN_FMT_P1 0
|
||||
#define TEGRA_WIN_FMT_P2 1
|
||||
#define TEGRA_WIN_FMT_P4 2
|
||||
#define TEGRA_WIN_FMT_P8 3
|
||||
#define TEGRA_WIN_FMT_B4G4R4A4 4
|
||||
#define TEGRA_WIN_FMT_B5G5R5A 5
|
||||
#define TEGRA_WIN_FMT_B5G6R5 6
|
||||
#define TEGRA_WIN_FMT_AB5G5R5 7
|
||||
#define TEGRA_WIN_FMT_B8G8R8A8 12
|
||||
#define TEGRA_WIN_FMT_R8G8B8A8 13
|
||||
#define TEGRA_WIN_FMT_B6x2G6x2R6x2A8 14
|
||||
#define TEGRA_WIN_FMT_R6x2G6x2B6x2A8 15
|
||||
#define TEGRA_WIN_FMT_YCbCr422 16
|
||||
#define TEGRA_WIN_FMT_YUV422 17
|
||||
#define TEGRA_WIN_FMT_YCbCr420P 18
|
||||
#define TEGRA_WIN_FMT_YUV420P 19
|
||||
#define TEGRA_WIN_FMT_YCbCr422P 20
|
||||
#define TEGRA_WIN_FMT_YUV422P 21
|
||||
#define TEGRA_WIN_FMT_YCbCr422R 22
|
||||
#define TEGRA_WIN_FMT_YUV422R 23
|
||||
#define TEGRA_WIN_FMT_YCbCr422RA 24
|
||||
#define TEGRA_WIN_FMT_YUV422RA 25
|
||||
|
||||
struct tegra_fb_data {
|
||||
int win;
|
||||
|
||||
int xres;
|
||||
int yres;
|
||||
int bits_per_pixel;
|
||||
};
|
||||
|
||||
struct tegra_dc_platform_data {
|
||||
unsigned long flags;
|
||||
struct tegra_dc_out *default_out;
|
||||
struct tegra_fb_data *fb;
|
||||
};
|
||||
|
||||
#define TEGRA_DC_FLAG_ENABLED (1 << 0)
|
||||
|
||||
struct tegra_dc *tegra_dc_get_dc(unsigned idx);
|
||||
struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win);
|
||||
|
||||
/* tegra_dc_update_windows and tegra_dc_sync_windows do not support windows
|
||||
* with differenct dcs in one call
|
||||
*/
|
||||
int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n);
|
||||
int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n);
|
||||
|
||||
/* will probably be replaced with an interface describing the window order */
|
||||
void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend);
|
||||
|
||||
int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode);
|
||||
|
||||
#endif
|
||||
39
arch/arm/mach-tegra/include/mach/fb.h
Normal file
39
arch/arm/mach-tegra/include/mach/fb.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* arch/arm/mach-tegra/include/mach/fb.h
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Erik Gilling <konkers@google.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA_FB_H
|
||||
#define __MACH_TEGRA_FB_H
|
||||
|
||||
#ifdef CONFIG_FB_TEGRA
|
||||
struct tegra_fb_info *tegra_fb_register(struct platform_device *pdev, struct tegra_dc *dc,
|
||||
struct tegra_fb_data *fb_data, struct resource *fb_mem);
|
||||
void tegra_fb_unregister(struct tegra_fb_info *fb_info);
|
||||
#else
|
||||
static inline struct tegra_fb_info *tegra_fb_register(struct platform_device *pdev, struct tegra_dc *dc,
|
||||
struct tegra_fb_data *fb_data, struct resource *fb_mem)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void tegra_fb_unregister(struct tegra_fb_info *fb_info)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -2256,6 +2256,7 @@ config FB_JZ4740
|
||||
|
||||
source "drivers/video/omap/Kconfig"
|
||||
source "drivers/video/omap2/Kconfig"
|
||||
source "drivers/video/tegra/Kconfig"
|
||||
|
||||
source "drivers/video/backlight/Kconfig"
|
||||
source "drivers/video/display/Kconfig"
|
||||
|
||||
@@ -134,6 +134,7 @@ obj-$(CONFIG_FB_MB862XX) += mb862xx/
|
||||
obj-$(CONFIG_FB_MSM) += msm/
|
||||
obj-$(CONFIG_FB_NUC900) += nuc900fb.o
|
||||
obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o
|
||||
obj-y += tegra/
|
||||
|
||||
# Platform or fallback drivers go here
|
||||
obj-$(CONFIG_FB_UVESA) += uvesafb.o
|
||||
|
||||
15
drivers/video/tegra/Kconfig
Normal file
15
drivers/video/tegra/Kconfig
Normal file
@@ -0,0 +1,15 @@
|
||||
config TEGRA_DC
|
||||
tristate "Tegra Display Contoller"
|
||||
depends on ARCH_TEGRA
|
||||
help
|
||||
Tegra display controller support.
|
||||
|
||||
config FB_TEGRA
|
||||
tristate "Tegra Framebuffer driver"
|
||||
depends on TEGRA_DC && FB = y
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
default FB
|
||||
help
|
||||
Framebuffer device support for the Tegra display controller.
|
||||
2
drivers/video/tegra/Makefile
Normal file
2
drivers/video/tegra/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_TEGRA_DC) += dc/
|
||||
obj-$(CONFIG_FB_TEGRA) += fb.o
|
||||
2
drivers/video/tegra/dc/Makefile
Normal file
2
drivers/video/tegra/dc/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
obj-y += dc.o
|
||||
obj-y += rgb.o
|
||||
889
drivers/video/tegra/dc/dc.c
Normal file
889
drivers/video/tegra/dc/dc.c
Normal file
@@ -0,0 +1,889 @@
|
||||
/*
|
||||
* drivers/video/tegra/dc/dc.c
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Author: Erik Gilling <konkers@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <mach/clk.h>
|
||||
#include <mach/dc.h>
|
||||
#include <mach/fb.h>
|
||||
|
||||
#include "dc_reg.h"
|
||||
#include "dc_priv.h"
|
||||
|
||||
struct tegra_dc_blend tegra_dc_blend_modes[][DC_N_WINDOWS] = {
|
||||
{{.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
|
||||
.one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
|
||||
.two_win_x = BLEND(NOKEY, FIX, 0x00, 0x00),
|
||||
.two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
|
||||
.three_win_xy = BLEND(NOKEY, FIX, 0x00, 0x00)},
|
||||
{.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
|
||||
.one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
|
||||
.two_win_x = BLEND(NOKEY, FIX, 0xff, 0xff),
|
||||
.two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
|
||||
.three_win_xy = BLEND(NOKEY, DEPENDANT, 0x00, 0x00)},
|
||||
{.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
|
||||
.one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
|
||||
.two_win_x = BLEND(NOKEY, ALPHA, 0xff, 0xff),
|
||||
.two_win_y = BLEND(NOKEY, ALPHA, 0xff, 0xff),
|
||||
.three_win_xy = BLEND(NOKEY, ALPHA, 0xff, 0xff)}
|
||||
}
|
||||
};
|
||||
|
||||
struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
|
||||
|
||||
DEFINE_MUTEX(tegra_dc_lock);
|
||||
|
||||
static inline int tegra_dc_fmt_bpp(int fmt)
|
||||
{
|
||||
switch(fmt) {
|
||||
case TEGRA_WIN_FMT_P1:
|
||||
return 1;
|
||||
|
||||
case TEGRA_WIN_FMT_P2:
|
||||
return 2;
|
||||
|
||||
case TEGRA_WIN_FMT_P4:
|
||||
return 4;
|
||||
|
||||
case TEGRA_WIN_FMT_P8:
|
||||
return 8;
|
||||
|
||||
case TEGRA_WIN_FMT_B4G4R4A4:
|
||||
case TEGRA_WIN_FMT_B5G5R5A:
|
||||
case TEGRA_WIN_FMT_B5G6R5:
|
||||
case TEGRA_WIN_FMT_AB5G5R5:
|
||||
return 16;
|
||||
|
||||
case TEGRA_WIN_FMT_B8G8R8A8:
|
||||
case TEGRA_WIN_FMT_R8G8B8A8:
|
||||
case TEGRA_WIN_FMT_B6x2G6x2R6x2A8:
|
||||
case TEGRA_WIN_FMT_R6x2G6x2B6x2A8:
|
||||
return 32;
|
||||
|
||||
case TEGRA_WIN_FMT_YCbCr422:
|
||||
case TEGRA_WIN_FMT_YUV422:
|
||||
case TEGRA_WIN_FMT_YCbCr420P:
|
||||
case TEGRA_WIN_FMT_YUV420P:
|
||||
case TEGRA_WIN_FMT_YCbCr422P:
|
||||
case TEGRA_WIN_FMT_YUV422P:
|
||||
case TEGRA_WIN_FMT_YCbCr422R:
|
||||
case TEGRA_WIN_FMT_YUV422R:
|
||||
case TEGRA_WIN_FMT_YCbCr422RA:
|
||||
case TEGRA_WIN_FMT_YUV422RA:
|
||||
/* FIXME: need to know the bpp of these formats */
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DUMP_REG(a) do { \
|
||||
snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
|
||||
#a, a, tegra_dc_readl(dc, a)); \
|
||||
print(data, buff); \
|
||||
} while (0)
|
||||
|
||||
static void _dump_regs(struct tegra_dc *dc, void *data,
|
||||
void (* print)(void *data, const char *str))
|
||||
{
|
||||
int i;
|
||||
char buff[256];
|
||||
|
||||
DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT);
|
||||
DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
|
||||
DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_ERROR);
|
||||
DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT);
|
||||
DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL);
|
||||
DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_ERROR);
|
||||
DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT);
|
||||
DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL);
|
||||
DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_ERROR);
|
||||
DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT);
|
||||
DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL);
|
||||
DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_ERROR);
|
||||
DUMP_REG(DC_CMD_CONT_SYNCPT_VSYNC);
|
||||
DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
|
||||
DUMP_REG(DC_CMD_DISPLAY_COMMAND);
|
||||
DUMP_REG(DC_CMD_SIGNAL_RAISE);
|
||||
DUMP_REG(DC_CMD_INT_STATUS);
|
||||
DUMP_REG(DC_CMD_INT_MASK);
|
||||
DUMP_REG(DC_CMD_INT_ENABLE);
|
||||
DUMP_REG(DC_CMD_INT_TYPE);
|
||||
DUMP_REG(DC_CMD_INT_POLARITY);
|
||||
DUMP_REG(DC_CMD_SIGNAL_RAISE1);
|
||||
DUMP_REG(DC_CMD_SIGNAL_RAISE2);
|
||||
DUMP_REG(DC_CMD_SIGNAL_RAISE3);
|
||||
DUMP_REG(DC_CMD_STATE_ACCESS);
|
||||
DUMP_REG(DC_CMD_STATE_CONTROL);
|
||||
DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER);
|
||||
DUMP_REG(DC_CMD_REG_ACT_CONTROL);
|
||||
|
||||
DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS0);
|
||||
DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS1);
|
||||
DUMP_REG(DC_DISP_DISP_WIN_OPTIONS);
|
||||
DUMP_REG(DC_DISP_MEM_HIGH_PRIORITY);
|
||||
DUMP_REG(DC_DISP_MEM_HIGH_PRIORITY_TIMER);
|
||||
DUMP_REG(DC_DISP_DISP_TIMING_OPTIONS);
|
||||
DUMP_REG(DC_DISP_REF_TO_SYNC);
|
||||
DUMP_REG(DC_DISP_SYNC_WIDTH);
|
||||
DUMP_REG(DC_DISP_BACK_PORCH);
|
||||
DUMP_REG(DC_DISP_DISP_ACTIVE);
|
||||
DUMP_REG(DC_DISP_FRONT_PORCH);
|
||||
DUMP_REG(DC_DISP_H_PULSE0_CONTROL);
|
||||
DUMP_REG(DC_DISP_H_PULSE0_POSITION_A);
|
||||
DUMP_REG(DC_DISP_H_PULSE0_POSITION_B);
|
||||
DUMP_REG(DC_DISP_H_PULSE0_POSITION_C);
|
||||
DUMP_REG(DC_DISP_H_PULSE0_POSITION_D);
|
||||
DUMP_REG(DC_DISP_H_PULSE1_CONTROL);
|
||||
DUMP_REG(DC_DISP_H_PULSE1_POSITION_A);
|
||||
DUMP_REG(DC_DISP_H_PULSE1_POSITION_B);
|
||||
DUMP_REG(DC_DISP_H_PULSE1_POSITION_C);
|
||||
DUMP_REG(DC_DISP_H_PULSE1_POSITION_D);
|
||||
DUMP_REG(DC_DISP_H_PULSE2_CONTROL);
|
||||
DUMP_REG(DC_DISP_H_PULSE2_POSITION_A);
|
||||
DUMP_REG(DC_DISP_H_PULSE2_POSITION_B);
|
||||
DUMP_REG(DC_DISP_H_PULSE2_POSITION_C);
|
||||
DUMP_REG(DC_DISP_H_PULSE2_POSITION_D);
|
||||
DUMP_REG(DC_DISP_V_PULSE0_CONTROL);
|
||||
DUMP_REG(DC_DISP_V_PULSE0_POSITION_A);
|
||||
DUMP_REG(DC_DISP_V_PULSE0_POSITION_B);
|
||||
DUMP_REG(DC_DISP_V_PULSE0_POSITION_C);
|
||||
DUMP_REG(DC_DISP_V_PULSE1_CONTROL);
|
||||
DUMP_REG(DC_DISP_V_PULSE1_POSITION_A);
|
||||
DUMP_REG(DC_DISP_V_PULSE1_POSITION_B);
|
||||
DUMP_REG(DC_DISP_V_PULSE1_POSITION_C);
|
||||
DUMP_REG(DC_DISP_V_PULSE2_CONTROL);
|
||||
DUMP_REG(DC_DISP_V_PULSE2_POSITION_A);
|
||||
DUMP_REG(DC_DISP_V_PULSE3_CONTROL);
|
||||
DUMP_REG(DC_DISP_V_PULSE3_POSITION_A);
|
||||
DUMP_REG(DC_DISP_M0_CONTROL);
|
||||
DUMP_REG(DC_DISP_M1_CONTROL);
|
||||
DUMP_REG(DC_DISP_DI_CONTROL);
|
||||
DUMP_REG(DC_DISP_PP_CONTROL);
|
||||
DUMP_REG(DC_DISP_PP_SELECT_A);
|
||||
DUMP_REG(DC_DISP_PP_SELECT_B);
|
||||
DUMP_REG(DC_DISP_PP_SELECT_C);
|
||||
DUMP_REG(DC_DISP_PP_SELECT_D);
|
||||
DUMP_REG(DC_DISP_DISP_CLOCK_CONTROL);
|
||||
DUMP_REG(DC_DISP_DISP_INTERFACE_CONTROL);
|
||||
DUMP_REG(DC_DISP_DISP_COLOR_CONTROL);
|
||||
DUMP_REG(DC_DISP_SHIFT_CLOCK_OPTIONS);
|
||||
DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS);
|
||||
DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS);
|
||||
DUMP_REG(DC_DISP_LCD_SPI_OPTIONS);
|
||||
DUMP_REG(DC_DISP_BORDER_COLOR);
|
||||
DUMP_REG(DC_DISP_COLOR_KEY0_LOWER);
|
||||
DUMP_REG(DC_DISP_COLOR_KEY0_UPPER);
|
||||
DUMP_REG(DC_DISP_COLOR_KEY1_LOWER);
|
||||
DUMP_REG(DC_DISP_COLOR_KEY1_UPPER);
|
||||
DUMP_REG(DC_DISP_CURSOR_FOREGROUND);
|
||||
DUMP_REG(DC_DISP_CURSOR_BACKGROUND);
|
||||
DUMP_REG(DC_DISP_CURSOR_START_ADDR);
|
||||
DUMP_REG(DC_DISP_CURSOR_START_ADDR_NS);
|
||||
DUMP_REG(DC_DISP_CURSOR_POSITION);
|
||||
DUMP_REG(DC_DISP_CURSOR_POSITION_NS);
|
||||
DUMP_REG(DC_DISP_INIT_SEQ_CONTROL);
|
||||
DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_A);
|
||||
DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_B);
|
||||
DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_C);
|
||||
DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_D);
|
||||
DUMP_REG(DC_DISP_DC_MCCIF_FIFOCTRL);
|
||||
DUMP_REG(DC_DISP_MCCIF_DISPLAY0A_HYST);
|
||||
DUMP_REG(DC_DISP_MCCIF_DISPLAY0B_HYST);
|
||||
DUMP_REG(DC_DISP_MCCIF_DISPLAY0C_HYST);
|
||||
DUMP_REG(DC_DISP_MCCIF_DISPLAY1B_HYST);
|
||||
DUMP_REG(DC_DISP_DAC_CRT_CTRL);
|
||||
DUMP_REG(DC_DISP_DISP_MISC_CONTROL);
|
||||
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
print(data, "\n");
|
||||
snprintf(buff, sizeof(buff), "WINDOW %c:\n", 'A' + i);
|
||||
print(data, buff);
|
||||
|
||||
tegra_dc_writel(dc, WINDOW_A_SELECT << i, DC_CMD_DISPLAY_WINDOW_HEADER);
|
||||
DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER);
|
||||
DUMP_REG(DC_WIN_WIN_OPTIONS);
|
||||
DUMP_REG(DC_WIN_BYTE_SWAP);
|
||||
DUMP_REG(DC_WIN_BUFFER_CONTROL);
|
||||
DUMP_REG(DC_WIN_COLOR_DEPTH);
|
||||
DUMP_REG(DC_WIN_POSITION);
|
||||
DUMP_REG(DC_WIN_SIZE);
|
||||
DUMP_REG(DC_WIN_PRESCALED_SIZE);
|
||||
DUMP_REG(DC_WIN_H_INITIAL_DDA);
|
||||
DUMP_REG(DC_WIN_V_INITIAL_DDA);
|
||||
DUMP_REG(DC_WIN_DDA_INCREMENT);
|
||||
DUMP_REG(DC_WIN_LINE_STRIDE);
|
||||
DUMP_REG(DC_WIN_BUF_STRIDE);
|
||||
DUMP_REG(DC_WIN_BLEND_NOKEY);
|
||||
DUMP_REG(DC_WIN_BLEND_1WIN);
|
||||
DUMP_REG(DC_WIN_BLEND_2WIN_X);
|
||||
DUMP_REG(DC_WIN_BLEND_2WIN_Y);
|
||||
DUMP_REG(DC_WIN_BLEND_3WIN_XY);
|
||||
DUMP_REG(DC_WINBUF_START_ADDR);
|
||||
DUMP_REG(DC_WINBUF_ADDR_H_OFFSET);
|
||||
DUMP_REG(DC_WINBUF_ADDR_V_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
#undef DUMP_REG
|
||||
|
||||
#ifdef DEBUG
|
||||
static void dump_regs_print(void *data, const char *str)
|
||||
{
|
||||
struct tegra_dc *dc = data;
|
||||
dev_dbg(&dc->pdev->dev, "%s", str);
|
||||
}
|
||||
|
||||
static void dump_regs(struct tegra_dc *dc)
|
||||
{
|
||||
_dump_regs(dc, dc, dump_regs_print);
|
||||
}
|
||||
#else
|
||||
|
||||
static void dump_regs(struct tegra_dc *dc) {}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static void dbg_regs_print(void *data, const char *str)
|
||||
{
|
||||
struct seq_file *s = data;
|
||||
|
||||
seq_printf(s, "%s", str);
|
||||
}
|
||||
|
||||
#undef DUMP_REG
|
||||
|
||||
static int dbg_dc_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct tegra_dc *dc = s->private;
|
||||
|
||||
_dump_regs(dc, s, dbg_regs_print);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int dbg_dc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dbg_dc_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations dbg_fops = {
|
||||
.open = dbg_dc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void tegra_dc_dbg_add(struct tegra_dc *dc)
|
||||
{
|
||||
char name[32];
|
||||
|
||||
snprintf(name, sizeof(name), "tegra_dc%d_regs", dc->pdev->id);
|
||||
(void) debugfs_create_file(name, S_IRUGO,NULL, dc, &dbg_fops);
|
||||
}
|
||||
#else
|
||||
static void tegra_dc_dbg_add(struct tegra_dc *dc) {}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static int tegra_dc_add(struct tegra_dc *dc, int index)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&tegra_dc_lock);
|
||||
if (index >= TEGRA_MAX_DC) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (tegra_dcs[index] != NULL) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tegra_dcs[index] = dc;
|
||||
|
||||
out:
|
||||
mutex_unlock(&tegra_dc_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct tegra_dc *tegra_dc_get_dc(unsigned idx)
|
||||
{
|
||||
if (idx < TEGRA_MAX_DC)
|
||||
return tegra_dcs[idx];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_dc_get_dc);
|
||||
|
||||
struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win)
|
||||
{
|
||||
if (win >= dc->n_windows)
|
||||
return NULL;
|
||||
|
||||
return &dc->windows[win];
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_dc_get_window);
|
||||
|
||||
/* does not support updating windows on multiple dcs in one call */
|
||||
int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
|
||||
{
|
||||
struct tegra_dc *dc;
|
||||
unsigned long update_mask = GENERAL_ACT_REQ;
|
||||
unsigned long val;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
dc = windows[0]->dc;
|
||||
|
||||
spin_lock_irqsave(&dc->lock, flags);
|
||||
for (i = 0; i < n; i++) {
|
||||
struct tegra_dc_win *win = windows[i];
|
||||
unsigned h_dda;
|
||||
unsigned v_dda;
|
||||
unsigned stride;
|
||||
|
||||
tegra_dc_writel(dc, WINDOW_A_SELECT << win->idx,
|
||||
DC_CMD_DISPLAY_WINDOW_HEADER);
|
||||
|
||||
update_mask |= WIN_A_ACT_REQ << win->idx;
|
||||
|
||||
if (!(win->flags & TEGRA_WIN_FLAG_ENABLED)) {
|
||||
tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS);
|
||||
continue;
|
||||
}
|
||||
|
||||
tegra_dc_writel(dc, win->fmt, DC_WIN_COLOR_DEPTH);
|
||||
tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
|
||||
|
||||
stride = win->w * tegra_dc_fmt_bpp(win->fmt) / 8;
|
||||
|
||||
/* TODO: implement filter on settings */
|
||||
h_dda = (win->w * 0x1000) / (win->out_w - 1);
|
||||
v_dda = (win->h * 0x1000) / (win->out_h - 1);
|
||||
|
||||
tegra_dc_writel(dc,
|
||||
V_POSITION(win->y) | H_POSITION(win->x),
|
||||
DC_WIN_POSITION);
|
||||
tegra_dc_writel(dc,
|
||||
V_SIZE(win->out_h) | H_SIZE(win->out_w),
|
||||
DC_WIN_SIZE);
|
||||
tegra_dc_writel(dc,
|
||||
V_PRESCALED_SIZE(win->out_h) |
|
||||
H_PRESCALED_SIZE(stride),
|
||||
DC_WIN_PRESCALED_SIZE);
|
||||
tegra_dc_writel(dc, 0, DC_WIN_H_INITIAL_DDA);
|
||||
tegra_dc_writel(dc, 0, DC_WIN_V_INITIAL_DDA);
|
||||
tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda),
|
||||
DC_WIN_DDA_INCREMENT);
|
||||
tegra_dc_writel(dc, stride, DC_WIN_LINE_STRIDE);
|
||||
tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
|
||||
|
||||
val = WIN_ENABLE;
|
||||
if (win->flags & TEGRA_WIN_FLAG_COLOR_EXPAND)
|
||||
val |= COLOR_EXPAND;
|
||||
tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS);
|
||||
|
||||
tegra_dc_writel(dc, (unsigned long)win->phys_addr, DC_WINBUF_START_ADDR);
|
||||
tegra_dc_writel(dc, 0, DC_WINBUF_ADDR_H_OFFSET);
|
||||
tegra_dc_writel(dc, 0, DC_WINBUF_ADDR_V_OFFSET);
|
||||
|
||||
win->dirty = 1;
|
||||
|
||||
}
|
||||
|
||||
tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
|
||||
|
||||
val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
|
||||
val |= FRAME_END_INT;
|
||||
tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
|
||||
|
||||
val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
|
||||
val |= FRAME_END_INT;
|
||||
tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
|
||||
|
||||
tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
|
||||
spin_unlock_irqrestore(&dc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_dc_update_windows);
|
||||
|
||||
static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
|
||||
int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (windows[i]->dirty)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* does not support syncing windows on multiple dcs in one call */
|
||||
int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
|
||||
{
|
||||
if (n < 1 || n > DC_N_WINDOWS)
|
||||
return -EINVAL;
|
||||
|
||||
return wait_event_interruptible_timeout(windows[0]->dc->wq,
|
||||
tegra_dc_windows_are_clean(windows, n),
|
||||
HZ);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_dc_sync_windows);
|
||||
|
||||
void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DC_N_WINDOWS; i++) {
|
||||
tegra_dc_writel(dc, WINDOW_A_SELECT << i, DC_CMD_DISPLAY_WINDOW_HEADER);
|
||||
tegra_dc_writel(dc, blend[i].nokey, DC_WIN_BLEND_NOKEY);
|
||||
tegra_dc_writel(dc, blend[i].one_win, DC_WIN_BLEND_1WIN);
|
||||
tegra_dc_writel(dc, blend[i].two_win_x, DC_WIN_BLEND_2WIN_X);
|
||||
tegra_dc_writel(dc, blend[i].two_win_y, DC_WIN_BLEND_2WIN_Y);
|
||||
tegra_dc_writel(dc, blend[i].three_win_xy, DC_WIN_BLEND_3WIN_XY);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_dc_set_blending);
|
||||
|
||||
int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
|
||||
{
|
||||
unsigned long val;
|
||||
unsigned long rate;
|
||||
unsigned long div;
|
||||
|
||||
tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
|
||||
tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16),
|
||||
DC_DISP_REF_TO_SYNC);
|
||||
tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16),
|
||||
DC_DISP_SYNC_WIDTH);
|
||||
tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16),
|
||||
DC_DISP_BACK_PORCH);
|
||||
tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16),
|
||||
DC_DISP_DISP_ACTIVE);
|
||||
tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16),
|
||||
DC_DISP_FRONT_PORCH);
|
||||
|
||||
tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL,
|
||||
DC_DISP_DATA_ENABLE_OPTIONS);
|
||||
|
||||
/* TODO: MIPI/CRT/HDMI clock cals */
|
||||
|
||||
val = DISP_DATA_FORMAT_DF1P1C;
|
||||
|
||||
if (dc->out->align == TEGRA_DC_ALIGN_MSB)
|
||||
val |= DISP_DATA_ALIGNMENT_MSB;
|
||||
else
|
||||
val |= DISP_DATA_ALIGNMENT_LSB;
|
||||
|
||||
if (dc->out->order == TEGRA_DC_ORDER_RED_BLUE)
|
||||
val |= DISP_DATA_ORDER_RED_BLUE;
|
||||
else
|
||||
val |= DISP_DATA_ORDER_BLUE_RED;
|
||||
|
||||
tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL);
|
||||
|
||||
rate = clk_get_rate(dc->clk);
|
||||
|
||||
div = ((rate * 2 + mode->pclk / 2) / mode->pclk) - 2;
|
||||
|
||||
if (rate * 2 / (div + 2) < (mode->pclk / 100 * 99) ||
|
||||
rate * 2 / (div + 2) > (mode->pclk / 100 * 109)) {
|
||||
dev_err(&dc->pdev->dev,
|
||||
"can't divide %ld clock to %d -1/+9%% %ld %d %d\n",
|
||||
rate, mode->pclk,
|
||||
rate / div, (mode->pclk / 100 * 99),
|
||||
(mode->pclk / 100 * 109));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tegra_dc_writel(dc, 0x00010001,
|
||||
DC_DISP_SHIFT_CLOCK_OPTIONS);
|
||||
tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div),
|
||||
DC_DISP_DISP_CLOCK_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_dc_set_mode);
|
||||
|
||||
static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
|
||||
{
|
||||
dc->out = out;
|
||||
|
||||
if (out->n_modes > 0)
|
||||
dc->mode = &dc->out->modes[0];
|
||||
else
|
||||
dev_err(&dc->pdev->dev, "No default modes specified. Leaving output disabled.\n");
|
||||
|
||||
switch (out->type) {
|
||||
case TEGRA_DC_OUT_RGB:
|
||||
dc->out_ops = &tegra_dc_rgb_ops;
|
||||
break;
|
||||
|
||||
default:
|
||||
dc->out_ops = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t tegra_dc_irq(int irq, void *ptr)
|
||||
{
|
||||
struct tegra_dc *dc = ptr;
|
||||
unsigned long status;
|
||||
unsigned long flags;
|
||||
unsigned long val;
|
||||
int i;
|
||||
|
||||
|
||||
status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
|
||||
tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
|
||||
|
||||
if (status & FRAME_END_INT) {
|
||||
int completed = 0;
|
||||
int dirty = 0;
|
||||
|
||||
spin_lock_irqsave(&dc->lock, flags);
|
||||
val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
|
||||
for (i = 0; i < DC_N_WINDOWS; i++) {
|
||||
if (!(val & (WIN_A_ACT_REQ << i))) {
|
||||
dc->windows[i].dirty = 0;
|
||||
completed = 1;
|
||||
} else {
|
||||
dirty = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dirty) {
|
||||
val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
|
||||
val &= ~FRAME_END_INT;
|
||||
tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dc->lock, flags);
|
||||
|
||||
if (completed)
|
||||
wake_up(&dc->wq);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void tegra_dc_init(struct tegra_dc *dc)
|
||||
{
|
||||
tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
|
||||
if (dc->pdev->id == 0)
|
||||
tegra_dc_writel(dc, 0x0000011a, DC_CMD_CONT_SYNCPT_VSYNC);
|
||||
else if (dc->pdev->id == 1)
|
||||
tegra_dc_writel(dc, 0x0000011b, DC_CMD_CONT_SYNCPT_VSYNC);
|
||||
tegra_dc_writel(dc, 0x00004700, DC_CMD_INT_TYPE);
|
||||
tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_POLARITY);
|
||||
tegra_dc_writel(dc, 0x00000020, DC_DISP_MEM_HIGH_PRIORITY);
|
||||
tegra_dc_writel(dc, 0x00000001, DC_DISP_MEM_HIGH_PRIORITY_TIMER);
|
||||
|
||||
tegra_dc_writel(dc, 0x0001c702, DC_CMD_INT_MASK);
|
||||
tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_ENABLE);
|
||||
|
||||
if (dc->mode)
|
||||
tegra_dc_set_mode(dc, dc->mode);
|
||||
|
||||
|
||||
if (dc->out_ops && dc->out_ops->init)
|
||||
dc->out_ops->init(dc);
|
||||
}
|
||||
|
||||
static int tegra_dc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_dc *dc;
|
||||
struct clk *clk;
|
||||
struct clk *host1x_clk;
|
||||
struct resource *res;
|
||||
struct resource *base_res;
|
||||
struct resource *fb_mem = NULL;
|
||||
int ret = 0;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
int i;
|
||||
|
||||
if (!pdev->dev.platform_data) {
|
||||
dev_err(&pdev->dev, "no platform data\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
dc = kzalloc(sizeof(struct tegra_dc), GFP_KERNEL);
|
||||
if (!dc) {
|
||||
dev_err(&pdev->dev, "can't allocate memory for tegra_dc\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "irq");
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "no irq\n");
|
||||
ret = -ENOENT;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no mem resource\n");
|
||||
ret = -ENOENT;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
base_res = request_mem_region(res->start, resource_size(res), pdev->name);
|
||||
if (!base_res) {
|
||||
dev_err(&pdev->dev, "request_mem_region failed\n");
|
||||
ret = -EBUSY;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
base = ioremap(res->start, resource_size(res));
|
||||
if (!base) {
|
||||
dev_err(&pdev->dev, "registers can't be mapped\n");
|
||||
ret = -EBUSY;
|
||||
goto err_release_resource_reg;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fbmem");
|
||||
if (res)
|
||||
fb_mem = request_mem_region(res->start, resource_size(res), pdev->name);
|
||||
|
||||
host1x_clk = clk_get(&pdev->dev, "host1x");
|
||||
if (IS_ERR_OR_NULL(host1x_clk)) {
|
||||
dev_err(&pdev->dev, "can't get host1x clock\n");
|
||||
ret = -ENOENT;
|
||||
goto err_iounmap_reg;
|
||||
}
|
||||
clk_enable(host1x_clk);
|
||||
|
||||
clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR_OR_NULL(clk)) {
|
||||
dev_err(&pdev->dev, "can't get clock\n");
|
||||
ret = -ENOENT;
|
||||
|
||||
goto err_put_host1x_clk;
|
||||
}
|
||||
clk_enable(clk);
|
||||
tegra_periph_reset_deassert(clk);
|
||||
|
||||
dc->clk = clk;
|
||||
dc->host1x_clk = host1x_clk;
|
||||
dc->base_res = base_res;
|
||||
dc->base = base;
|
||||
dc->irq = irq;
|
||||
dc->pdev = pdev;
|
||||
dc->pdata = pdev->dev.platform_data;
|
||||
spin_lock_init(&dc->lock);
|
||||
init_waitqueue_head(&dc->wq);
|
||||
|
||||
|
||||
dc->n_windows = DC_N_WINDOWS;
|
||||
for (i = 0; i < dc->n_windows; i++) {
|
||||
dc->windows[i].idx = i;
|
||||
dc->windows[i].dc = dc;
|
||||
}
|
||||
|
||||
if (request_irq(irq, tegra_dc_irq, IRQF_DISABLED,
|
||||
dev_name(&pdev->dev), dc)) {
|
||||
dev_err(&pdev->dev, "request_irq %d failed\n", irq);
|
||||
ret = -EBUSY;
|
||||
goto err_put_clk;
|
||||
}
|
||||
|
||||
ret = tegra_dc_add(dc, pdev->id);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can't add dc\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) {
|
||||
if (dc->pdata->default_out)
|
||||
tegra_dc_set_out(dc, dc->pdata->default_out);
|
||||
else
|
||||
dev_err(&pdev->dev, "No default output specified. Leaving output disabled.\n");
|
||||
}
|
||||
|
||||
tegra_dc_init(dc);
|
||||
|
||||
tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
|
||||
|
||||
platform_set_drvdata(pdev, dc);
|
||||
|
||||
tegra_dc_dbg_add(dc);
|
||||
|
||||
dev_info(&pdev->dev, "probed\n");
|
||||
|
||||
if (fb_mem && dc->pdata->fb) {
|
||||
dc->fb = tegra_fb_register(pdev, dc, dc->pdata->fb, fb_mem);
|
||||
if (IS_ERR_OR_NULL(dc->fb))
|
||||
dc->fb = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(irq, dc);
|
||||
err_put_clk:
|
||||
clk_disable(clk);
|
||||
clk_put(clk);
|
||||
err_put_host1x_clk:
|
||||
clk_disable(host1x_clk);
|
||||
clk_put(host1x_clk);
|
||||
err_iounmap_reg:
|
||||
iounmap(base);
|
||||
if (fb_mem)
|
||||
release_resource(fb_mem);
|
||||
err_release_resource_reg:
|
||||
release_resource(base_res);
|
||||
err_free:
|
||||
kfree(dc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_dc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_dc *dc = platform_get_drvdata(pdev);
|
||||
|
||||
if (dc->fb) {
|
||||
tegra_fb_unregister(dc->fb);
|
||||
release_resource(dc->fb_mem);
|
||||
}
|
||||
|
||||
free_irq(dc->irq, dc);
|
||||
tegra_periph_reset_assert(dc->clk);
|
||||
clk_disable(dc->clk);
|
||||
clk_put(dc->clk);
|
||||
clk_disable(dc->host1x_clk);
|
||||
clk_put(dc->host1x_clk);
|
||||
iounmap(dc->base);
|
||||
release_resource(dc->base_res);
|
||||
kfree(dc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tegra_dc_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct tegra_dc *dc = platform_get_drvdata(pdev);
|
||||
|
||||
dev_info(&pdev->dev, "suspend\n");
|
||||
|
||||
disable_irq(dc->irq);
|
||||
tegra_periph_reset_assert(dc->clk);
|
||||
clk_disable(dc->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_dc_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_dc *dc = platform_get_drvdata(pdev);
|
||||
struct tegra_dc_win *wins[DC_N_WINDOWS];
|
||||
int i;
|
||||
|
||||
dev_info(&pdev->dev, "resume\n");
|
||||
|
||||
clk_enable(dc->clk);
|
||||
tegra_periph_reset_deassert(dc->clk);
|
||||
enable_irq(dc->irq);
|
||||
|
||||
for (i = 0; i < dc->n_windows; i++)
|
||||
wins[i] = &dc->windows[i];
|
||||
|
||||
tegra_dc_init(dc);
|
||||
|
||||
tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
|
||||
tegra_dc_update_windows(wins, dc->n_windows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern int suspend_set(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
if (!strcmp(val, "dump"))
|
||||
dump_regs(tegra_dcs[0]);
|
||||
#ifdef CONFIG_PM
|
||||
else if (!strcmp(val, "suspend"))
|
||||
tegra_dc_suspend(tegra_dcs[0]->pdev, PMSG_SUSPEND);
|
||||
else if (!strcmp(val, "resume"))
|
||||
tegra_dc_resume(tegra_dcs[0]->pdev);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int suspend_get(char *buffer, struct kernel_param *kp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int suspend;
|
||||
|
||||
module_param_call(suspend, suspend_set, suspend_get, &suspend, 0644);
|
||||
|
||||
struct platform_driver tegra_dc_driver = {
|
||||
.driver = {
|
||||
.name = "tegradc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tegra_dc_probe,
|
||||
.remove = tegra_dc_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = tegra_dc_suspend,
|
||||
.resume = tegra_dc_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init tegra_dc_module_init(void)
|
||||
{
|
||||
return platform_driver_register(&tegra_dc_driver);
|
||||
}
|
||||
|
||||
static void __exit tegra_dc_module_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tegra_dc_driver);
|
||||
}
|
||||
|
||||
module_exit(tegra_dc_module_exit);
|
||||
module_init(tegra_dc_module_init);
|
||||
86
drivers/video/tegra/dc/dc_priv.h
Normal file
86
drivers/video/tegra/dc/dc_priv.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* drivers/video/tegra/dc/dc_priv.h
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Author: Erik Gilling <konkers@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H
|
||||
#define __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H
|
||||
|
||||
#include <linux/wait.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
struct tegra_dc;
|
||||
|
||||
struct tegra_dc_out_ops {
|
||||
void (* init)(struct tegra_dc *dc);
|
||||
};
|
||||
|
||||
struct tegra_dc {
|
||||
struct list_head list;
|
||||
|
||||
struct platform_device *pdev;
|
||||
struct tegra_dc_platform_data *pdata;
|
||||
|
||||
struct resource *base_res;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
|
||||
struct clk *clk;
|
||||
struct clk *host1x_clk;
|
||||
|
||||
struct tegra_dc_out *out;
|
||||
struct tegra_dc_out_ops *out_ops;
|
||||
|
||||
struct tegra_dc_mode *mode;
|
||||
|
||||
struct tegra_dc_win windows[DC_N_WINDOWS];
|
||||
int n_windows;
|
||||
|
||||
wait_queue_head_t wq;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
struct resource *fb_mem;
|
||||
struct tegra_fb_info *fb;
|
||||
};
|
||||
|
||||
static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
|
||||
unsigned long reg)
|
||||
{
|
||||
return readl(dc->base + reg * 4);
|
||||
}
|
||||
|
||||
static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long val,
|
||||
unsigned long reg)
|
||||
{
|
||||
writel(val, dc->base + reg * 4);
|
||||
}
|
||||
|
||||
static inline void _tegra_dc_write_table(struct tegra_dc *dc, const u32 *table,
|
||||
unsigned len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
tegra_dc_writel(dc, table[i * 2 + 1], table[i * 2]);
|
||||
}
|
||||
|
||||
#define tegra_dc_write_table(dc, table) \
|
||||
_tegra_dc_write_table(dc, table, ARRAY_SIZE(table) / 2)
|
||||
|
||||
extern struct tegra_dc_out_ops tegra_dc_rgb_ops;
|
||||
|
||||
#endif
|
||||
342
drivers/video/tegra/dc/dc_reg.h
Normal file
342
drivers/video/tegra/dc/dc_reg.h
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* drivers/video/tegra/dc/dc_reg.h
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Author: Erik Gilling <konkers@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_REG_H
|
||||
#define __DRIVERS_VIDEO_TEGRA_DC_DC_REG_H
|
||||
|
||||
#define DC_CMD_GENERAL_INCR_SYNCPT 0x000
|
||||
#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x001
|
||||
#define DC_CMD_GENERAL_INCR_SYNCPT_ERROR 0x002
|
||||
#define DC_CMD_WIN_A_INCR_SYNCPT 0x008
|
||||
#define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL 0x009
|
||||
#define DC_CMD_WIN_A_INCR_SYNCPT_ERROR 0x00a
|
||||
#define DC_CMD_WIN_B_INCR_SYNCPT 0x010
|
||||
#define DC_CMD_WIN_B_INCR_SYNCPT_CNTRL 0x011
|
||||
#define DC_CMD_WIN_B_INCR_SYNCPT_ERROR 0x012
|
||||
#define DC_CMD_WIN_C_INCR_SYNCPT 0x018
|
||||
#define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL 0x019
|
||||
#define DC_CMD_WIN_C_INCR_SYNCPT_ERROR 0x01a
|
||||
#define DC_CMD_CONT_SYNCPT_VSYNC 0x028
|
||||
#define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031
|
||||
#define DC_CMD_DISPLAY_COMMAND 0x032
|
||||
#define DISP_COMMAND_RAISE (1 << 0)
|
||||
#define DISP_CTRL_MODE_STOP (0 << 5)
|
||||
#define DISP_CTRL_MODE_C_DISPLAY (1 << 5)
|
||||
#define DISP_CTRL_MODE_NC_DISPLAY (2 << 5)
|
||||
#define DISP_COMMAND_RAISE_VECTOR(x) (((x) & 0x1f) << 22)
|
||||
#define DISP_COMMAND_RAISE_CHANNEL_ID(x) (((x) & 0xf) << 27)
|
||||
|
||||
#define DC_CMD_SIGNAL_RAISE 0x033
|
||||
#define DC_CMD_DISPLAY_POWER_CONTROL 0x036
|
||||
#define PW0_ENABLE (1 << 0)
|
||||
#define PW1_ENABLE (1 << 2)
|
||||
#define PW2_ENABLE (1 << 4)
|
||||
#define PW3_ENABLE (1 << 6)
|
||||
#define PW4_ENABLE (1 << 8)
|
||||
#define PM0_ENABLE (1 << 16)
|
||||
#define PM1_ENABLE (1 << 18)
|
||||
#define SPI_ENABLE (1 << 24)
|
||||
#define HSPI_ENABLE (1 << 25)
|
||||
|
||||
#define DC_CMD_INT_STATUS 0x037
|
||||
#define DC_CMD_INT_MASK 0x038
|
||||
#define DC_CMD_INT_ENABLE 0x039
|
||||
#define DC_CMD_INT_TYPE 0x03a
|
||||
#define DC_CMD_INT_POLARITY 0x03b
|
||||
#define CTXSW_INT (1 << 0)
|
||||
#define FRAME_END_INT (1 << 1)
|
||||
#define V_BLANK_INT (1 << 2)
|
||||
#define H_BLANK_INT (1 << 3)
|
||||
#define V_PULSE3_INT (1 << 4)
|
||||
#define SPI_BUSY_INT (1 << 7)
|
||||
#define WIN_A_UF_INT (1 << 8)
|
||||
#define WIN_B_UF_INT (1 << 9)
|
||||
#define WIN_C_UF_INT (1 << 10)
|
||||
#define MSF_INT (1 << 12)
|
||||
#define SSF_INT (1 << 13)
|
||||
#define WIN_A_OF_INT (1 << 14)
|
||||
#define WIN_B_OF_INT (1 << 15)
|
||||
#define WIN_C_OF_INT (1 << 16)
|
||||
#define GPIO_0_INT (1 << 18)
|
||||
#define GPIO_1_INT (1 << 19)
|
||||
#define GPIO_2_INT (1 << 20)
|
||||
|
||||
#define DC_CMD_SIGNAL_RAISE1 0x03c
|
||||
#define DC_CMD_SIGNAL_RAISE2 0x03d
|
||||
#define DC_CMD_SIGNAL_RAISE3 0x03e
|
||||
#define DC_CMD_STATE_ACCESS 0x040
|
||||
#define DC_CMD_STATE_CONTROL 0x041
|
||||
#define GENERAL_ACT_REQ (1 << 0)
|
||||
#define WIN_A_ACT_REQ (1 << 1)
|
||||
#define WIN_B_ACT_REQ (1 << 2)
|
||||
#define WIN_C_ACT_REQ (1 << 3)
|
||||
|
||||
#define DC_CMD_DISPLAY_WINDOW_HEADER 0x042
|
||||
#define WINDOW_A_SELECT (1 << 4)
|
||||
#define WINDOW_B_SELECT (1 << 5)
|
||||
#define WINDOW_C_SELECT (1 << 6)
|
||||
|
||||
#define DC_CMD_REG_ACT_CONTROL 0x043
|
||||
|
||||
#define DC_COM_CRC_CONTROL 0x300
|
||||
#define DC_COM_CRC_CHECKSUM 0x301
|
||||
#define DC_COM_PIN_OUTPUT_ENABLE0 0x302
|
||||
#define DC_COM_PIN_OUTPUT_ENABLE1 0x303
|
||||
#define DC_COM_PIN_OUTPUT_ENABLE2 0x304
|
||||
#define DC_COM_PIN_OUTPUT_ENABLE3 0x305
|
||||
#define DC_COM_PIN_OUTPUT_POLARITY0 0x306
|
||||
#define DC_COM_PIN_OUTPUT_POLARITY1 0x307
|
||||
#define DC_COM_PIN_OUTPUT_POLARITY2 0x308
|
||||
#define DC_COM_PIN_OUTPUT_POLARITY3 0x309
|
||||
#define DC_COM_PIN_OUTPUT_DATA0 0x30a
|
||||
#define DC_COM_PIN_OUTPUT_DATA1 0x30b
|
||||
#define DC_COM_PIN_OUTPUT_DATA2 0x30c
|
||||
#define DC_COM_PIN_OUTPUT_DATA3 0x30d
|
||||
#define DC_COM_PIN_INPUT_ENABLE0 0x30e
|
||||
#define DC_COM_PIN_INPUT_ENABLE1 0x30f
|
||||
#define DC_COM_PIN_INPUT_ENABLE2 0x310
|
||||
#define DC_COM_PIN_INPUT_ENABLE3 0x311
|
||||
#define DC_COM_PIN_INPUT_DATA0 0x312
|
||||
#define DC_COM_PIN_INPUT_DATA1 0x313
|
||||
#define DC_COM_PIN_OUTPUT_SELECT0 0x314
|
||||
#define DC_COM_PIN_OUTPUT_SELECT1 0x315
|
||||
#define DC_COM_PIN_OUTPUT_SELECT2 0x316
|
||||
#define DC_COM_PIN_OUTPUT_SELECT3 0x317
|
||||
#define DC_COM_PIN_OUTPUT_SELECT4 0x318
|
||||
#define DC_COM_PIN_OUTPUT_SELECT5 0x319
|
||||
#define DC_COM_PIN_OUTPUT_SELECT6 0x31a
|
||||
#define DC_COM_PIN_MISC_CONTROL 0x31b
|
||||
#define DC_COM_PM0_CONTROL 0x31c
|
||||
#define DC_COM_PM0_DUTY_CYCLE 0x31d
|
||||
#define DC_COM_PM1_CONTROL 0x31e
|
||||
#define DC_COM_PM1_DUTY_CYCLE 0x31f
|
||||
#define DC_COM_SPI_CONTROL 0x320
|
||||
#define DC_COM_SPI_START_BYTE 0x321
|
||||
#define DC_COM_HSPI_WRITE_DATA_AB 0x322
|
||||
#define DC_COM_HSPI_WRITE_DATA_CD 0x323
|
||||
#define DC_COM_HSPI_CS_DC 0x324
|
||||
#define DC_COM_SCRATCH_REGISTER_A 0x325
|
||||
#define DC_COM_SCRATCH_REGISTER_B 0x326
|
||||
#define DC_COM_GPIO_CTRL 0x327
|
||||
#define DC_COM_GPIO_DEBOUNCE_COUNTER 0x328
|
||||
#define DC_COM_CRC_CHECKSUM_LATCHED 0x329
|
||||
|
||||
#define DC_DISP_DISP_SIGNAL_OPTIONS0 0x400
|
||||
#define DC_DISP_DISP_SIGNAL_OPTIONS1 0x401
|
||||
#define DC_DISP_DISP_WIN_OPTIONS 0x402
|
||||
#define DC_DISP_MEM_HIGH_PRIORITY 0x403
|
||||
#define DC_DISP_MEM_HIGH_PRIORITY_TIMER 0x404
|
||||
#define DC_DISP_DISP_TIMING_OPTIONS 0x405
|
||||
#define DC_DISP_REF_TO_SYNC 0x406
|
||||
#define DC_DISP_SYNC_WIDTH 0x407
|
||||
#define DC_DISP_BACK_PORCH 0x408
|
||||
#define DC_DISP_DISP_ACTIVE 0x409
|
||||
#define DC_DISP_FRONT_PORCH 0x40a
|
||||
#define DC_DISP_H_PULSE0_CONTROL 0x40b
|
||||
#define DC_DISP_H_PULSE0_POSITION_A 0x40c
|
||||
#define DC_DISP_H_PULSE0_POSITION_B 0x40d
|
||||
#define DC_DISP_H_PULSE0_POSITION_C 0x40e
|
||||
#define DC_DISP_H_PULSE0_POSITION_D 0x40f
|
||||
#define DC_DISP_H_PULSE1_CONTROL 0x410
|
||||
#define DC_DISP_H_PULSE1_POSITION_A 0x411
|
||||
#define DC_DISP_H_PULSE1_POSITION_B 0x412
|
||||
#define DC_DISP_H_PULSE1_POSITION_C 0x413
|
||||
#define DC_DISP_H_PULSE1_POSITION_D 0x414
|
||||
#define DC_DISP_H_PULSE2_CONTROL 0x415
|
||||
#define DC_DISP_H_PULSE2_POSITION_A 0x416
|
||||
#define DC_DISP_H_PULSE2_POSITION_B 0x417
|
||||
#define DC_DISP_H_PULSE2_POSITION_C 0x418
|
||||
#define DC_DISP_H_PULSE2_POSITION_D 0x419
|
||||
#define DC_DISP_V_PULSE0_CONTROL 0x41a
|
||||
#define DC_DISP_V_PULSE0_POSITION_A 0x41b
|
||||
#define DC_DISP_V_PULSE0_POSITION_B 0x41c
|
||||
#define DC_DISP_V_PULSE0_POSITION_C 0x41d
|
||||
#define DC_DISP_V_PULSE1_CONTROL 0x41e
|
||||
#define DC_DISP_V_PULSE1_POSITION_A 0x41f
|
||||
#define DC_DISP_V_PULSE1_POSITION_B 0x420
|
||||
#define DC_DISP_V_PULSE1_POSITION_C 0x421
|
||||
#define DC_DISP_V_PULSE2_CONTROL 0x422
|
||||
#define DC_DISP_V_PULSE2_POSITION_A 0x423
|
||||
#define DC_DISP_V_PULSE3_CONTROL 0x424
|
||||
#define DC_DISP_V_PULSE3_POSITION_A 0x425
|
||||
#define DC_DISP_M0_CONTROL 0x426
|
||||
#define DC_DISP_M1_CONTROL 0x427
|
||||
#define DC_DISP_DI_CONTROL 0x428
|
||||
#define DC_DISP_PP_CONTROL 0x429
|
||||
#define DC_DISP_PP_SELECT_A 0x42a
|
||||
#define DC_DISP_PP_SELECT_B 0x42b
|
||||
#define DC_DISP_PP_SELECT_C 0x42c
|
||||
#define DC_DISP_PP_SELECT_D 0x42d
|
||||
#define DC_DISP_DISP_CLOCK_CONTROL 0x42e
|
||||
#define PIXEL_CLK_DIVIDER_PCD1 (0 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD1H (1 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD2 (2 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD3 (3 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD4 (4 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD6 (5 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD8 (6 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD9 (7 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD12 (8 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD16 (9 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD18 (10 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD24 (11 << 8)
|
||||
#define PIXEL_CLK_DIVIDER_PCD13 (12 << 8)
|
||||
#define SHIFT_CLK_DIVIDER(x) ((x) & 0xff)
|
||||
|
||||
#define DC_DISP_DISP_INTERFACE_CONTROL 0x42f
|
||||
#define DISP_DATA_FORMAT_DF1P1C (0 << 0)
|
||||
#define DISP_DATA_FORMAT_DF1P2C24B (1 << 0)
|
||||
#define DISP_DATA_FORMAT_DF1P2C18B (2 << 0)
|
||||
#define DISP_DATA_FORMAT_DF1P2C16B (3 << 0)
|
||||
#define DISP_DATA_FORMAT_DF2S (5 << 0)
|
||||
#define DISP_DATA_FORMAT_DF3S (6 << 0)
|
||||
#define DISP_DATA_FORMAT_DFSPI (7 << 0)
|
||||
#define DISP_DATA_FORMAT_DF1P3C24B (8 << 0)
|
||||
#define DISP_DATA_FORMAT_DF1P3C18B (9 << 0)
|
||||
#define DISP_DATA_ALIGNMENT_MSB (0 << 8)
|
||||
#define DISP_DATA_ALIGNMENT_LSB (1 << 8)
|
||||
#define DISP_DATA_ORDER_RED_BLUE (0 << 9)
|
||||
#define DISP_DATA_ORDER_BLUE_RED (1 << 9)
|
||||
|
||||
#define DC_DISP_DISP_COLOR_CONTROL 0x430
|
||||
#define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431
|
||||
#define DC_DISP_DATA_ENABLE_OPTIONS 0x432
|
||||
#define DE_SELECT_ACTIVE_BLANK 0x0
|
||||
#define DE_SELECT_ACTIVE 0x1
|
||||
#define DE_SELECT_ACTIVE_IS 0x2
|
||||
#define DE_CONTROL_ONECLK (0 << 2)
|
||||
#define DE_CONTROL_NORMAL (1 << 2)
|
||||
#define DE_CONTROL_EARLY_EXT (2 << 2)
|
||||
#define DE_CONTROL_EARLY (3 << 2)
|
||||
#define DE_CONTROL_ACTIVE_BLANK (4 << 2)
|
||||
|
||||
#define DC_DISP_SERIAL_INTERFACE_OPTIONS 0x433
|
||||
#define DC_DISP_LCD_SPI_OPTIONS 0x434
|
||||
#define DC_DISP_BORDER_COLOR 0x435
|
||||
#define DC_DISP_COLOR_KEY0_LOWER 0x436
|
||||
#define DC_DISP_COLOR_KEY0_UPPER 0x437
|
||||
#define DC_DISP_COLOR_KEY1_LOWER 0x438
|
||||
#define DC_DISP_COLOR_KEY1_UPPER 0x439
|
||||
#define DC_DISP_CURSOR_FOREGROUND 0x43c
|
||||
#define DC_DISP_CURSOR_BACKGROUND 0x43d
|
||||
#define DC_DISP_CURSOR_START_ADDR 0x43e
|
||||
#define DC_DISP_CURSOR_START_ADDR_NS 0x43f
|
||||
#define DC_DISP_CURSOR_POSITION 0x440
|
||||
#define DC_DISP_CURSOR_POSITION_NS 0x441
|
||||
#define DC_DISP_INIT_SEQ_CONTROL 0x442
|
||||
#define DC_DISP_SPI_INIT_SEQ_DATA_A 0x443
|
||||
#define DC_DISP_SPI_INIT_SEQ_DATA_B 0x444
|
||||
#define DC_DISP_SPI_INIT_SEQ_DATA_C 0x445
|
||||
#define DC_DISP_SPI_INIT_SEQ_DATA_D 0x446
|
||||
#define DC_DISP_DC_MCCIF_FIFOCTRL 0x480
|
||||
#define DC_DISP_MCCIF_DISPLAY0A_HYST 0x481
|
||||
#define DC_DISP_MCCIF_DISPLAY0B_HYST 0x482
|
||||
#define DC_DISP_MCCIF_DISPLAY0C_HYST 0x483
|
||||
#define DC_DISP_MCCIF_DISPLAY1B_HYST 0x484
|
||||
#define DC_DISP_DAC_CRT_CTRL 0x4c0
|
||||
#define DC_DISP_DISP_MISC_CONTROL 0x4c1
|
||||
|
||||
#define DC_WINC_COLOR_PALETTE(x) (0x500 + (x))
|
||||
|
||||
#define DC_WINC_PALETTE_COLOR_EXT 0x600
|
||||
#define DC_WINC_H_FILTER_P(x) (0x601 + (x))
|
||||
#define DC_WINC_CSC_YOF 0x611
|
||||
#define DC_WINC_CSC_KYRGB 0x612
|
||||
#define DC_WINC_CSC_KUR 0x613
|
||||
#define DC_WINC_CSC_KVR 0x614
|
||||
#define DC_WINC_CSC_KUG 0x615
|
||||
#define DC_WINC_CSC_KVG 0x616
|
||||
#define DC_WINC_CSC_KUB 0x617
|
||||
#define DC_WINC_CSC_KVB 0x618
|
||||
#define DC_WINC_V_FILTER_P(x) (0x619 + (x))
|
||||
#define DC_WIN_WIN_OPTIONS 0x700
|
||||
#define H_DIRECTION_INCREMENT (0 << 0)
|
||||
#define H_DIRECTION_DECREMENTT (1 << 0)
|
||||
#define V_DIRECTION_INCREMENT (0 << 2)
|
||||
#define V_DIRECTION_DECREMENTT (1 << 2)
|
||||
#define COLOR_EXPAND (1 << 6)
|
||||
#define CP_ENABLE (1 << 16)
|
||||
#define DV_ENABLE (1 << 20)
|
||||
#define WIN_ENABLE (1 << 30)
|
||||
|
||||
#define DC_WIN_BYTE_SWAP 0x701
|
||||
#define BYTE_SWAP_NOSWAP 0
|
||||
#define BYTE_SWAP_SWAP2 1
|
||||
#define BYTE_SWAP_SWAP4 2
|
||||
#define BYTE_SWAP_SWAP4HW 3
|
||||
|
||||
#define DC_WIN_BUFFER_CONTROL 0x702
|
||||
#define BUFFER_CONTROL_HOST 0
|
||||
#define BUFFER_CONTROL_VI 1
|
||||
#define BUFFER_CONTROL_EPP 2
|
||||
#define BUFFER_CONTROL_MPEGE 3
|
||||
#define BUFFER_CONTROL_SB2D 4
|
||||
|
||||
#define DC_WIN_COLOR_DEPTH 0x703
|
||||
|
||||
#define DC_WIN_POSITION 0x704
|
||||
#define H_POSITION(x) (((x) & 0xfff) << 0)
|
||||
#define V_POSITION(x) (((x) & 0xfff) << 16)
|
||||
|
||||
#define DC_WIN_SIZE 0x705
|
||||
#define H_SIZE(x) (((x) & 0xfff) << 0)
|
||||
#define V_SIZE(x) (((x) & 0xfff) << 16)
|
||||
|
||||
#define DC_WIN_PRESCALED_SIZE 0x706
|
||||
#define H_PRESCALED_SIZE(x) (((x) & 0x3fff) << 0)
|
||||
#define V_PRESCALED_SIZE(x) (((x) & 0xfff) << 16)
|
||||
|
||||
#define DC_WIN_H_INITIAL_DDA 0x707
|
||||
#define DC_WIN_V_INITIAL_DDA 0x708
|
||||
#define DC_WIN_DDA_INCREMENT 0x709
|
||||
#define H_DDA_INC(x) (((x) & 0xffff) << 0)
|
||||
#define V_DDA_INC(x) (((x) & 0xffff) << 16)
|
||||
|
||||
#define DC_WIN_LINE_STRIDE 0x70a
|
||||
#define DC_WIN_BUF_STRIDE 0x70b
|
||||
#define DC_WIN_UV_BUF_STRIDE 0x70c
|
||||
#define DC_WIN_BUFFER_ADDR_MODE 0x70d
|
||||
#define DC_WIN_DV_CONTROL 0x70e
|
||||
#define DC_WIN_BLEND_NOKEY 0x70f
|
||||
#define DC_WIN_BLEND_1WIN 0x710
|
||||
#define DC_WIN_BLEND_2WIN_X 0x711
|
||||
#define DC_WIN_BLEND_2WIN_Y 0x712
|
||||
#define DC_WIN_BLEND_3WIN_XY 0x713
|
||||
#define CKEY_NOKEY (0 << 0)
|
||||
#define CKEY_KEY0 (1 << 0)
|
||||
#define CKEY_KEY1 (2 << 0)
|
||||
#define CKEY_KEY01 (3 << 0)
|
||||
#define BLEND_CONTROL_FIX (0 << 2)
|
||||
#define BLEND_CONTROL_ALPHA (1 << 2)
|
||||
#define BLEND_CONTROL_DEPENDANT (2 << 2)
|
||||
#define BLEND_WEIGHT0(x) (((x) & 0xff) << 8)
|
||||
#define BLEND_WEIGHT1(x) (((x) & 0xff) << 16)
|
||||
|
||||
#define DC_WIN_HP_FETCH_CONTROL 0x714
|
||||
#define DC_WINBUF_START_ADDR 0x800
|
||||
#define DC_WINBUF_START_ADDR_NS 0x801
|
||||
#define DC_WINBUF_START_ADDR_U 0x802
|
||||
#define DC_WINBUF_START_ADDR_U_NS 0x803
|
||||
#define DC_WINBUF_START_ADDR_V 0x804
|
||||
#define DC_WINBUF_START_ADDR_V_NS 0x805
|
||||
#define DC_WINBUF_ADDR_H_OFFSET 0x806
|
||||
#define DC_WINBUF_ADDR_H_OFFSET_NS 0x807
|
||||
#define DC_WINBUF_ADDR_V_OFFSET 0x808
|
||||
#define DC_WINBUF_ADDR_V_OFFSET_NS 0x809
|
||||
#define DC_WINBUF_UFLOW_STATUS 0x80a
|
||||
|
||||
#endif
|
||||
63
drivers/video/tegra/dc/rgb.c
Normal file
63
drivers/video/tegra/dc/rgb.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* drivers/video/tegra/dc/rgb.c
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Author: Erik Gilling <konkers@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <mach/dc.h>
|
||||
|
||||
#include "dc_reg.h"
|
||||
#include "dc_priv.h"
|
||||
|
||||
|
||||
static const u32 tegra_dc_rgb_pintable[] = {
|
||||
DC_COM_PIN_OUTPUT_ENABLE0, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_ENABLE1, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_ENABLE2, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_ENABLE3, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_POLARITY0, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_POLARITY1, 0x01000000,
|
||||
DC_COM_PIN_OUTPUT_POLARITY2, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_POLARITY3, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_DATA0, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_DATA1, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_DATA2, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_DATA3, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_SELECT0, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_SELECT1, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_SELECT2, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_SELECT3, 0x00000000,
|
||||
DC_COM_PIN_OUTPUT_SELECT4, 0x00210222,
|
||||
DC_COM_PIN_OUTPUT_SELECT5, 0x00002200,
|
||||
DC_COM_PIN_OUTPUT_SELECT6, 0x00020000,
|
||||
};
|
||||
|
||||
|
||||
void tegra_dc_rgb_init(struct tegra_dc *dc)
|
||||
{
|
||||
tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
|
||||
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
|
||||
DC_CMD_DISPLAY_POWER_CONTROL);
|
||||
|
||||
tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND);
|
||||
|
||||
tegra_dc_write_table(dc, tegra_dc_rgb_pintable);
|
||||
}
|
||||
|
||||
struct tegra_dc_out_ops tegra_dc_rgb_ops = {
|
||||
.init = tegra_dc_rgb_init,
|
||||
};
|
||||
|
||||
307
drivers/video/tegra/fb.c
Normal file
307
drivers/video/tegra/fb.c
Normal file
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* drivers/video/tegra/fb.c
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Author: Erik Gilling <konkers@android.com>
|
||||
* Colin Cross <ccross@android.com>
|
||||
* Travis Geiselbrecht <travis@palm.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#include <mach/dc.h>
|
||||
#include <mach/fb.h>
|
||||
|
||||
struct tegra_fb_info {
|
||||
struct tegra_dc_win *win;
|
||||
struct platform_device *pdev;
|
||||
struct fb_info *info;
|
||||
|
||||
struct resource *fb_mem;
|
||||
|
||||
int xres;
|
||||
int yres;
|
||||
|
||||
atomic_t in_use;
|
||||
};
|
||||
|
||||
/* palette array used by the fbcon */
|
||||
static u32 pseudo_palette[16];
|
||||
|
||||
static int tegra_fb_open(struct fb_info *info, int user)
|
||||
{
|
||||
struct tegra_fb_info *tegra_fb = info->par;
|
||||
|
||||
if (atomic_xchg(&tegra_fb->in_use, 1))
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_fb_release(struct fb_info *info, int user)
|
||||
{
|
||||
struct tegra_fb_info *tegra_fb = info->par;
|
||||
|
||||
WARN_ON(!atomic_xchg(&tegra_fb->in_use, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
|
||||
{
|
||||
if ((var->xres != info->var.xres) ||
|
||||
(var->yres != info->var.yres) ||
|
||||
(var->xres_virtual != info->var.xres_virtual) ||
|
||||
(var->yres_virtual != info->var.yres_virtual) ||
|
||||
(var->grayscale != info->var.grayscale))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_fb_set_par(struct fb_info *info)
|
||||
{
|
||||
struct tegra_fb_info *tegra_fb = info->par;
|
||||
struct fb_var_screeninfo *var = &info->var;
|
||||
|
||||
/* we only support RGB ordering for now */
|
||||
switch (var->bits_per_pixel) {
|
||||
case 32:
|
||||
case 24:
|
||||
var->red.offset = 0;
|
||||
var->red.length = 8;
|
||||
var->green.offset = 8;
|
||||
var->green.length = 8;
|
||||
var->blue.offset = 16;
|
||||
var->blue.length = 8;
|
||||
tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
|
||||
break;
|
||||
case 16:
|
||||
var->red.offset = 11;
|
||||
var->red.length = 5;
|
||||
var->green.offset = 5;
|
||||
var->green.length = 6;
|
||||
var->blue.offset = 0;
|
||||
var->blue.length = 5;
|
||||
tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
info->fix.line_length = var->xres * var->bits_per_pixel / 8;
|
||||
|
||||
tegra_dc_update_windows(&tegra_fb->win, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
|
||||
unsigned blue, unsigned transp, struct fb_info *info)
|
||||
{
|
||||
struct fb_var_screeninfo *var = &info->var;
|
||||
|
||||
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
||||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
|
||||
u32 v;
|
||||
|
||||
if (regno >= 16)
|
||||
return -EINVAL;
|
||||
|
||||
v = (red << var->red.offset) |
|
||||
(green << var->green.offset) |
|
||||
(blue << var->blue.offset);
|
||||
|
||||
((u32 *)info->pseudo_palette)[regno] = v;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
|
||||
{
|
||||
struct tegra_fb_info *tegra_fb = info->par;
|
||||
char __iomem *flush_start;
|
||||
char __iomem *flush_end;
|
||||
u32 addr;
|
||||
|
||||
flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
|
||||
flush_end = flush_start + (var->yres * info->fix.line_length);
|
||||
/* do we need to dma flush here? */
|
||||
|
||||
info->var.xoffset = var->xoffset;
|
||||
info->var.yoffset = var->yoffset;
|
||||
|
||||
addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
|
||||
(var->xoffset * (var->bits_per_pixel/8));
|
||||
|
||||
tegra_fb->win->phys_addr = addr;
|
||||
/* TODO: update virt_addr */
|
||||
|
||||
tegra_dc_update_windows(&tegra_fb->win, 1);
|
||||
tegra_dc_sync_windows(&tegra_fb->win, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
|
||||
{
|
||||
cfb_fillrect(info, rect);
|
||||
}
|
||||
|
||||
static void tegra_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
|
||||
{
|
||||
cfb_copyarea(info, region);
|
||||
}
|
||||
|
||||
static void tegra_fb_imageblit(struct fb_info *info, const struct fb_image *image)
|
||||
{
|
||||
cfb_imageblit(info, image);
|
||||
}
|
||||
|
||||
/* TODO: implement ALLOC, FREE, BLANK ioctls */
|
||||
/* TODO: implement private window ioctls to set overlay x,y */
|
||||
|
||||
static struct fb_ops tegra_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_open = tegra_fb_open,
|
||||
.fb_release = tegra_fb_release,
|
||||
.fb_check_var = tegra_fb_check_var,
|
||||
.fb_set_par = tegra_fb_set_par,
|
||||
.fb_setcolreg = tegra_fb_setcolreg,
|
||||
.fb_pan_display = tegra_fb_pan_display,
|
||||
.fb_fillrect = tegra_fb_fillrect,
|
||||
.fb_copyarea = tegra_fb_copyarea,
|
||||
.fb_imageblit = tegra_fb_imageblit,
|
||||
};
|
||||
|
||||
struct tegra_fb_info *tegra_fb_register(struct platform_device *pdev, struct tegra_dc *dc,
|
||||
struct tegra_fb_data *fb_data, struct resource *fb_mem)
|
||||
{
|
||||
struct tegra_dc_win *win;
|
||||
struct fb_info *info;
|
||||
struct tegra_fb_info *tegra_fb;
|
||||
void __iomem *fb_base;
|
||||
unsigned long fb_size;
|
||||
unsigned long fb_phys;
|
||||
int ret = 0;
|
||||
|
||||
win = tegra_dc_get_window(dc, fb_data->win);
|
||||
if (!win) {
|
||||
dev_err(&pdev->dev, "dc does not have a window at index %d\n", fb_data->win);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
info = framebuffer_alloc(sizeof(struct tegra_fb_info), &pdev->dev);
|
||||
if (!info) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
fb_size = resource_size(fb_mem);
|
||||
fb_phys = fb_mem->start;
|
||||
fb_base = ioremap_nocache(fb_phys, fb_size);
|
||||
if (!fb_base) {
|
||||
dev_err(&pdev->dev, "fb can't be mapped\n");
|
||||
ret = -EBUSY;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
tegra_fb = info->par;
|
||||
tegra_fb->win = win;
|
||||
tegra_fb->pdev = pdev;
|
||||
tegra_fb->fb_mem = fb_mem;
|
||||
tegra_fb->xres = fb_data->xres;
|
||||
tegra_fb->yres = fb_data->yres;
|
||||
atomic_set(&tegra_fb->in_use, 0);
|
||||
|
||||
info->fbops = &tegra_fb_ops;
|
||||
info->pseudo_palette = pseudo_palette;
|
||||
info->screen_base = fb_base;
|
||||
info->screen_size = fb_size;
|
||||
|
||||
strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id));
|
||||
info->fix.type = FB_TYPE_PACKED_PIXELS;
|
||||
info->fix.visual = FB_VISUAL_TRUECOLOR;
|
||||
info->fix.xpanstep = 1;
|
||||
info->fix.ypanstep = 1;
|
||||
info->fix.accel = FB_ACCEL_NONE;
|
||||
info->fix.smem_start = fb_phys;
|
||||
info->fix.smem_len = fb_size;
|
||||
|
||||
info->var.xres = fb_data->xres;
|
||||
info->var.yres = fb_data->yres;
|
||||
info->var.xres_virtual = fb_data->xres;
|
||||
info->var.yres_virtual = fb_data->yres*2;
|
||||
info->var.bits_per_pixel = fb_data->bits_per_pixel;
|
||||
info->var.activate = FB_ACTIVATE_VBL;
|
||||
/* TODO: fill in the following by querying the DC */
|
||||
info->var.height = -1;
|
||||
info->var.width = -1;
|
||||
info->var.pixclock = 24500;
|
||||
info->var.left_margin = 0;
|
||||
info->var.right_margin = 0;
|
||||
info->var.upper_margin = 0;
|
||||
info->var.lower_margin = 0;
|
||||
info->var.hsync_len = 0;
|
||||
info->var.vsync_len = 0;
|
||||
info->var.vmode = FB_VMODE_NONINTERLACED;
|
||||
|
||||
win->x = 0;
|
||||
win->y = 0;
|
||||
win->w = fb_data->xres;
|
||||
win->h = fb_data->yres;
|
||||
/* TODO: set to output res dc */
|
||||
win->out_w = fb_data->xres;
|
||||
win->out_h = fb_data->yres;
|
||||
win->phys_addr = fb_phys;
|
||||
win->virt_addr = fb_base;
|
||||
win->flags = TEGRA_WIN_FLAG_ENABLED | TEGRA_WIN_FLAG_COLOR_EXPAND;
|
||||
|
||||
tegra_fb_set_par(info);
|
||||
|
||||
if (register_framebuffer(info)) {
|
||||
dev_err(&pdev->dev, "failed to register framebuffer\n");
|
||||
ret = -ENODEV;
|
||||
goto err_iounmap_fb;
|
||||
}
|
||||
|
||||
tegra_fb->info = info;
|
||||
|
||||
dev_info(&pdev->dev, "probed\n");
|
||||
|
||||
return tegra_fb;
|
||||
|
||||
err_iounmap_fb:
|
||||
iounmap(fb_base);
|
||||
err_free:
|
||||
framebuffer_release(info);
|
||||
err:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void tegra_fb_unregister(struct tegra_fb_info *fb_info)
|
||||
{
|
||||
struct fb_info *info = fb_info->info;
|
||||
|
||||
unregister_framebuffer(info);
|
||||
iounmap(info->screen_base);
|
||||
framebuffer_release(info);
|
||||
}
|
||||
Reference in New Issue
Block a user