GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-$(CONFIG_EXYNOS_DP) += exynos_dp.o exynos_dp_lowlevel.o
obj-$(CONFIG_EXYNOS_FB) += exynos_fb.o
obj-$(CONFIG_EXYNOS_MIPI_DSIM) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
exynos_mipi_dsi_lowlevel.o
obj-$(CONFIG_EXYNOS_PWM_BL) += exynos_pwm_bl.o
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#ifndef _EXYNOS_EDP_LOWLEVEL_H
#define _EXYNOS_EDP_LOWLEVEL_H
void exynos_dp_enable_video_bist(struct exynos_dp *dp_regs,
unsigned int enable);
void exynos_dp_enable_video_mute(struct exynos_dp *dp_regs,
unsigned int enable);
void exynos_dp_reset(struct exynos_dp *dp_regs);
void exynos_dp_enable_sw_func(struct exynos_dp *dp_regs, unsigned int enable);
unsigned int exynos_dp_set_analog_power_down(struct exynos_dp *dp_regs,
unsigned int block, u32 enable);
unsigned int exynos_dp_get_pll_lock_status(struct exynos_dp *dp_regs);
int exynos_dp_init_analog_func(struct exynos_dp *dp_regs);
void exynos_dp_init_hpd(struct exynos_dp *dp_regs);
void exynos_dp_init_aux(struct exynos_dp *dp_regs);
void exynos_dp_config_interrupt(struct exynos_dp *dp_regs);
unsigned int exynos_dp_get_plug_in_status(struct exynos_dp *dp_regs);
unsigned int exynos_dp_detect_hpd(struct exynos_dp *dp_regs);
unsigned int exynos_dp_start_aux_transaction(struct exynos_dp *dp_regs);
unsigned int exynos_dp_write_byte_to_dpcd(struct exynos_dp *dp_regs,
unsigned int reg_addr,
unsigned char data);
unsigned int exynos_dp_read_byte_from_dpcd(struct exynos_dp *dp_regs,
unsigned int reg_addr,
unsigned char *data);
unsigned int exynos_dp_write_bytes_to_dpcd(struct exynos_dp *dp_regs,
unsigned int reg_addr,
unsigned int count,
unsigned char data[]);
unsigned int exynos_dp_read_bytes_from_dpcd(struct exynos_dp *dp_regs,
unsigned int reg_addr,
unsigned int count,
unsigned char data[]);
int exynos_dp_select_i2c_device(struct exynos_dp *dp_regs,
unsigned int device_addr,
unsigned int reg_addr);
int exynos_dp_read_byte_from_i2c(struct exynos_dp *dp_regs,
unsigned int device_addr,
unsigned int reg_addr, unsigned int *data);
int exynos_dp_read_bytes_from_i2c(struct exynos_dp *dp_regs,
unsigned int device_addr,
unsigned int reg_addr, unsigned int count,
unsigned char edid[]);
void exynos_dp_reset_macro(struct exynos_dp *dp_regs);
void exynos_dp_set_link_bandwidth(struct exynos_dp *dp_regs,
unsigned char bwtype);
unsigned char exynos_dp_get_link_bandwidth(struct exynos_dp *dp_regs);
void exynos_dp_set_lane_count(struct exynos_dp *dp_regs, unsigned char count);
unsigned int exynos_dp_get_lane_count(struct exynos_dp *dp_regs);
unsigned char exynos_dp_get_lanex_pre_emphasis(struct exynos_dp *dp_regs,
unsigned char lanecnt);
void exynos_dp_set_lane_pre_emphasis(struct exynos_dp *dp_regs,
unsigned int level, unsigned char lanecnt);
void exynos_dp_set_lanex_pre_emphasis(struct exynos_dp *dp_regs,
unsigned char request_val,
unsigned char lanecnt);
void exynos_dp_set_training_pattern(struct exynos_dp *dp_regs,
unsigned int pattern);
void exynos_dp_enable_enhanced_mode(struct exynos_dp *dp_regs,
unsigned char enable);
void exynos_dp_enable_scrambling(struct exynos_dp *dp_regs,
unsigned int enable);
int exynos_dp_init_video(struct exynos_dp *dp_regs);
void exynos_dp_config_video_slave_mode(struct exynos_dp *dp_regs,
struct edp_video_info *video_info);
void exynos_dp_set_video_color_format(struct exynos_dp *dp_regs,
struct edp_video_info *video_info);
int exynos_dp_config_video_bist(struct exynos_dp *dp_regs,
struct exynos_dp_priv *priv);
unsigned int exynos_dp_is_slave_video_stream_clock_on(
struct exynos_dp *dp_regs);
void exynos_dp_set_video_cr_mn(struct exynos_dp *dp_regs, unsigned int type,
unsigned int m_value, unsigned int n_value);
void exynos_dp_set_video_timing_mode(struct exynos_dp *dp_regs,
unsigned int type);
void exynos_dp_enable_video_master(struct exynos_dp *dp_regs,
unsigned int enable);
void exynos_dp_start_video(struct exynos_dp *dp_regs);
unsigned int exynos_dp_is_video_stream_on(struct exynos_dp *dp_regs);
#endif /* _EXYNOS_DP_LOWLEVEL_H */
@@ -0,0 +1,719 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <config.h>
#include <common.h>
#include <display.h>
#include <div64.h>
#include <dm.h>
#include <fdtdec.h>
#include <linux/libfdt.h>
#include <panel.h>
#include <video.h>
#include <video_bridge.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clock.h>
#include <asm/arch/clk.h>
#include <asm/arch/mipi_dsim.h>
#include <asm/arch/dp_info.h>
#include <asm/arch/fb.h>
#include <asm/arch/pinmux.h>
#include <asm/arch/system.h>
#include <asm/gpio.h>
#include <linux/errno.h>
DECLARE_GLOBAL_DATA_PTR;
enum {
FIMD_RGB_INTERFACE = 1,
FIMD_CPU_INTERFACE = 2,
};
enum exynos_fb_rgb_mode_t {
MODE_RGB_P = 0,
MODE_BGR_P = 1,
MODE_RGB_S = 2,
MODE_BGR_S = 3,
};
struct exynos_fb_priv {
ushort vl_col; /* Number of columns (i.e. 640) */
ushort vl_row; /* Number of rows (i.e. 480) */
ushort vl_rot; /* Rotation of Display (0, 1, 2, 3) */
ushort vl_width; /* Width of display area in millimeters */
ushort vl_height; /* Height of display area in millimeters */
/* LCD configuration register */
u_char vl_freq; /* Frequency */
u_char vl_clkp; /* Clock polarity */
u_char vl_oep; /* Output Enable polarity */
u_char vl_hsp; /* Horizontal Sync polarity */
u_char vl_vsp; /* Vertical Sync polarity */
u_char vl_dp; /* Data polarity */
u_char vl_bpix; /* Bits per pixel */
/* Horizontal control register. Timing from data sheet */
u_char vl_hspw; /* Horz sync pulse width */
u_char vl_hfpd; /* Wait before of line */
u_char vl_hbpd; /* Wait end of line */
/* Vertical control register. */
u_char vl_vspw; /* Vertical sync pulse width */
u_char vl_vfpd; /* Wait before of frame */
u_char vl_vbpd; /* Wait end of frame */
u_char vl_cmd_allow_len; /* Wait end of frame */
unsigned int win_id;
unsigned int init_delay;
unsigned int power_on_delay;
unsigned int reset_delay;
unsigned int interface_mode;
unsigned int mipi_enabled;
unsigned int dp_enabled;
unsigned int cs_setup;
unsigned int wr_setup;
unsigned int wr_act;
unsigned int wr_hold;
unsigned int logo_on;
unsigned int logo_width;
unsigned int logo_height;
int logo_x_offset;
int logo_y_offset;
unsigned long logo_addr;
unsigned int rgb_mode;
unsigned int resolution;
/* parent clock name(MPLL, EPLL or VPLL) */
unsigned int pclk_name;
/* ratio value for source clock from parent clock. */
unsigned int sclk_div;
unsigned int dual_lcd_enabled;
struct exynos_fb *reg;
struct exynos_platform_mipi_dsim *dsim_platform_data_dt;
};
static void exynos_fimd_set_dualrgb(struct exynos_fb_priv *priv, bool enabled)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
if (enabled) {
cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT |
EXYNOS_DUALRGB_VDEN_EN_ENABLE;
/* in case of Line Split mode, MAIN_CNT doesn't neet to set. */
cfg |= EXYNOS_DUALRGB_SUB_CNT(priv->vl_col / 2) |
EXYNOS_DUALRGB_MAIN_CNT(0);
}
writel(cfg, &reg->dualrgb);
}
static void exynos_fimd_set_dp_clkcon(struct exynos_fb_priv *priv,
unsigned int enabled)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
if (enabled)
cfg = EXYNOS_DP_CLK_ENABLE;
writel(cfg, &reg->dp_mie_clkcon);
}
static void exynos_fimd_set_par(struct exynos_fb_priv *priv,
unsigned int win_id)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
/* set window control */
cfg = readl((unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE |
EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE |
EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK |
EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK);
/* DATAPATH is DMA */
cfg |= EXYNOS_WINCON_DATAPATH_DMA;
cfg |= EXYNOS_WINCON_HAWSWP_ENABLE;
/* dma burst is 16 */
cfg |= EXYNOS_WINCON_BURSTLEN_16WORD;
switch (priv->vl_bpix) {
case 4:
cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565;
break;
default:
cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888;
break;
}
writel(cfg, (unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
/* set window position to x=0, y=0*/
cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0);
writel(cfg, (unsigned int)&reg->vidosd0a +
EXYNOS_VIDOSD(win_id));
cfg = EXYNOS_VIDOSD_RIGHT_X(priv->vl_col - 1) |
EXYNOS_VIDOSD_BOTTOM_Y(priv->vl_row - 1) |
EXYNOS_VIDOSD_RIGHT_X_E(1) |
EXYNOS_VIDOSD_BOTTOM_Y_E(0);
writel(cfg, (unsigned int)&reg->vidosd0b +
EXYNOS_VIDOSD(win_id));
/* set window size for window0*/
cfg = EXYNOS_VIDOSD_SIZE(priv->vl_col * priv->vl_row);
writel(cfg, (unsigned int)&reg->vidosd0c +
EXYNOS_VIDOSD(win_id));
}
static void exynos_fimd_set_buffer_address(struct exynos_fb_priv *priv,
unsigned int win_id,
ulong lcd_base_addr)
{
struct exynos_fb *reg = priv->reg;
unsigned long start_addr, end_addr;
start_addr = lcd_base_addr;
end_addr = start_addr + ((priv->vl_col * (VNBITS(priv->vl_bpix) / 8)) *
priv->vl_row);
writel(start_addr, (unsigned int)&reg->vidw00add0b0 +
EXYNOS_BUFFER_OFFSET(win_id));
writel(end_addr, (unsigned int)&reg->vidw00add1b0 +
EXYNOS_BUFFER_OFFSET(win_id));
}
static void exynos_fimd_set_clock(struct exynos_fb_priv *priv)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0, div = 0, remainder, remainder_div;
unsigned long pixel_clock;
unsigned long long src_clock;
if (priv->dual_lcd_enabled) {
pixel_clock = priv->vl_freq *
(priv->vl_hspw + priv->vl_hfpd +
priv->vl_hbpd + priv->vl_col / 2) *
(priv->vl_vspw + priv->vl_vfpd +
priv->vl_vbpd + priv->vl_row);
} else if (priv->interface_mode == FIMD_CPU_INTERFACE) {
pixel_clock = priv->vl_freq *
priv->vl_width * priv->vl_height *
(priv->cs_setup + priv->wr_setup +
priv->wr_act + priv->wr_hold + 1);
} else {
pixel_clock = priv->vl_freq *
(priv->vl_hspw + priv->vl_hfpd +
priv->vl_hbpd + priv->vl_col) *
(priv->vl_vspw + priv->vl_vfpd +
priv->vl_vbpd + priv->vl_row);
}
cfg = readl(&reg->vidcon0);
cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK |
EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK |
EXYNOS_VIDCON0_CLKDIR_MASK);
cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS |
EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED);
src_clock = (unsigned long long) get_lcd_clk();
/* get quotient and remainder. */
remainder = do_div(src_clock, pixel_clock);
div = src_clock;
remainder *= 10;
remainder_div = remainder / pixel_clock;
/* round about one places of decimals. */
if (remainder_div >= 5)
div++;
/* in case of dual lcd mode. */
if (priv->dual_lcd_enabled)
div--;
cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1);
writel(cfg, &reg->vidcon0);
}
void exynos_set_trigger(struct exynos_fb_priv *priv)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
cfg = readl(&reg->trigcon);
cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG);
writel(cfg, &reg->trigcon);
}
int exynos_is_i80_frame_done(struct exynos_fb_priv *priv)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
int status;
cfg = readl(&reg->trigcon);
/* frame done func is valid only when TRIMODE[0] is set to 1. */
status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) ==
EXYNOS_I80STATUS_TRIG_DONE;
return status;
}
static void exynos_fimd_lcd_on(struct exynos_fb_priv *priv)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
/* display on */
cfg = readl(&reg->vidcon0);
cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE);
writel(cfg, &reg->vidcon0);
}
static void exynos_fimd_window_on(struct exynos_fb_priv *priv,
unsigned int win_id)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
/* enable window */
cfg = readl((unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
cfg |= EXYNOS_WINCON_ENWIN_ENABLE;
writel(cfg, (unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
cfg = readl(&reg->winshmap);
cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id);
writel(cfg, &reg->winshmap);
}
void exynos_fimd_lcd_off(struct exynos_fb_priv *priv)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
cfg = readl(&reg->vidcon0);
cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE);
writel(cfg, &reg->vidcon0);
}
void exynos_fimd_window_off(struct exynos_fb_priv *priv, unsigned int win_id)
{
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0;
cfg = readl((unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
cfg &= EXYNOS_WINCON_ENWIN_DISABLE;
writel(cfg, (unsigned int)&reg->wincon0 +
EXYNOS_WINCON(win_id));
cfg = readl(&reg->winshmap);
cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id);
writel(cfg, &reg->winshmap);
}
/*
* The reset value for FIMD SYSMMU register MMU_CTRL is 3
* on Exynos5420 and newer versions.
* This means FIMD SYSMMU is on by default on Exynos5420
* and newer versions.
* Since in u-boot we don't use SYSMMU, we should disable
* those FIMD SYSMMU.
* Note that there are 2 SYSMMU for FIMD: m0 and m1.
* m0 handles windows 0 and 4, and m1 handles windows 1, 2 and 3.
* We disable both of them here.
*/
void exynos_fimd_disable_sysmmu(void)
{
u32 *sysmmufimd;
unsigned int node;
int node_list[2];
int count;
int i;
count = fdtdec_find_aliases_for_id(gd->fdt_blob, "fimd",
COMPAT_SAMSUNG_EXYNOS_SYSMMU, node_list, 2);
for (i = 0; i < count; i++) {
node = node_list[i];
if (node <= 0) {
debug("Can't get device node for fimd sysmmu\n");
return;
}
sysmmufimd = (u32 *)fdtdec_get_addr(gd->fdt_blob, node, "reg");
if (!sysmmufimd) {
debug("Can't get base address for sysmmu fimdm0");
return;
}
writel(0x0, sysmmufimd);
}
}
void exynos_fimd_lcd_init(struct udevice *dev)
{
struct exynos_fb_priv *priv = dev_get_priv(dev);
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct exynos_fb *reg = priv->reg;
unsigned int cfg = 0, rgb_mode;
unsigned int offset;
unsigned int node;
node = dev_of_offset(dev);
if (fdtdec_get_bool(gd->fdt_blob, node, "samsung,disable-sysmmu"))
exynos_fimd_disable_sysmmu();
offset = exynos_fimd_get_base_offset();
rgb_mode = priv->rgb_mode;
if (priv->interface_mode == FIMD_RGB_INTERFACE) {
cfg |= EXYNOS_VIDCON0_VIDOUT_RGB;
writel(cfg, &reg->vidcon0);
cfg = readl(&reg->vidcon2);
cfg &= ~(EXYNOS_VIDCON2_WB_MASK |
EXYNOS_VIDCON2_TVFORMATSEL_MASK |
EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK);
cfg |= EXYNOS_VIDCON2_WB_DISABLE;
writel(cfg, &reg->vidcon2);
/* set polarity */
cfg = 0;
if (!priv->vl_clkp)
cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE;
if (!priv->vl_hsp)
cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT;
if (!priv->vl_vsp)
cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT;
if (!priv->vl_dp)
cfg |= EXYNOS_VIDCON1_IVDEN_INVERT;
writel(cfg, (unsigned int)&reg->vidcon1 + offset);
/* set timing */
cfg = EXYNOS_VIDTCON0_VFPD(priv->vl_vfpd - 1);
cfg |= EXYNOS_VIDTCON0_VBPD(priv->vl_vbpd - 1);
cfg |= EXYNOS_VIDTCON0_VSPW(priv->vl_vspw - 1);
writel(cfg, (unsigned int)&reg->vidtcon0 + offset);
cfg = EXYNOS_VIDTCON1_HFPD(priv->vl_hfpd - 1);
cfg |= EXYNOS_VIDTCON1_HBPD(priv->vl_hbpd - 1);
cfg |= EXYNOS_VIDTCON1_HSPW(priv->vl_hspw - 1);
writel(cfg, (unsigned int)&reg->vidtcon1 + offset);
/* set lcd size */
cfg = EXYNOS_VIDTCON2_HOZVAL(priv->vl_col - 1) |
EXYNOS_VIDTCON2_LINEVAL(priv->vl_row - 1) |
EXYNOS_VIDTCON2_HOZVAL_E(priv->vl_col - 1) |
EXYNOS_VIDTCON2_LINEVAL_E(priv->vl_row - 1);
writel(cfg, (unsigned int)&reg->vidtcon2 + offset);
}
/* set display mode */
cfg = readl(&reg->vidcon0);
cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK;
cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT);
writel(cfg, &reg->vidcon0);
/* set par */
exynos_fimd_set_par(priv, priv->win_id);
/* set memory address */
exynos_fimd_set_buffer_address(priv, priv->win_id, plat->base);
/* set buffer size */
cfg = EXYNOS_VIDADDR_PAGEWIDTH(priv->vl_col *
VNBITS(priv->vl_bpix) / 8) |
EXYNOS_VIDADDR_PAGEWIDTH_E(priv->vl_col *
VNBITS(priv->vl_bpix) / 8) |
EXYNOS_VIDADDR_OFFSIZE(0) |
EXYNOS_VIDADDR_OFFSIZE_E(0);
writel(cfg, (unsigned int)&reg->vidw00add2 +
EXYNOS_BUFFER_SIZE(priv->win_id));
/* set clock */
exynos_fimd_set_clock(priv);
/* set rgb mode to dual lcd. */
exynos_fimd_set_dualrgb(priv, priv->dual_lcd_enabled);
/* display on */
exynos_fimd_lcd_on(priv);
/* window on */
exynos_fimd_window_on(priv, priv->win_id);
exynos_fimd_set_dp_clkcon(priv, priv->dp_enabled);
}
unsigned long exynos_fimd_calc_fbsize(struct exynos_fb_priv *priv)
{
return priv->vl_col * priv->vl_row * (VNBITS(priv->vl_bpix) / 8);
}
int exynos_fb_ofdata_to_platdata(struct udevice *dev)
{
struct exynos_fb_priv *priv = dev_get_priv(dev);
unsigned int node = dev_of_offset(dev);
const void *blob = gd->fdt_blob;
fdt_addr_t addr;
addr = devfdt_get_addr(dev);
if (addr == FDT_ADDR_T_NONE) {
debug("Can't get the FIMD base address\n");
return -EINVAL;
}
priv->reg = (struct exynos_fb *)addr;
priv->vl_col = fdtdec_get_int(blob, node, "samsung,vl-col", 0);
if (priv->vl_col == 0) {
debug("Can't get XRES\n");
return -ENXIO;
}
priv->vl_row = fdtdec_get_int(blob, node, "samsung,vl-row", 0);
if (priv->vl_row == 0) {
debug("Can't get YRES\n");
return -ENXIO;
}
priv->vl_width = fdtdec_get_int(blob, node,
"samsung,vl-width", 0);
priv->vl_height = fdtdec_get_int(blob, node,
"samsung,vl-height", 0);
priv->vl_freq = fdtdec_get_int(blob, node, "samsung,vl-freq", 0);
if (priv->vl_freq == 0) {
debug("Can't get refresh rate\n");
return -ENXIO;
}
if (fdtdec_get_bool(blob, node, "samsung,vl-clkp"))
priv->vl_clkp = VIDEO_ACTIVE_LOW;
if (fdtdec_get_bool(blob, node, "samsung,vl-oep"))
priv->vl_oep = VIDEO_ACTIVE_LOW;
if (fdtdec_get_bool(blob, node, "samsung,vl-hsp"))
priv->vl_hsp = VIDEO_ACTIVE_LOW;
if (fdtdec_get_bool(blob, node, "samsung,vl-vsp"))
priv->vl_vsp = VIDEO_ACTIVE_LOW;
if (fdtdec_get_bool(blob, node, "samsung,vl-dp"))
priv->vl_dp = VIDEO_ACTIVE_LOW;
priv->vl_bpix = fdtdec_get_int(blob, node, "samsung,vl-bpix", 0);
if (priv->vl_bpix == 0) {
debug("Can't get bits per pixel\n");
return -ENXIO;
}
priv->vl_hspw = fdtdec_get_int(blob, node, "samsung,vl-hspw", 0);
if (priv->vl_hspw == 0) {
debug("Can't get hsync width\n");
return -ENXIO;
}
priv->vl_hfpd = fdtdec_get_int(blob, node, "samsung,vl-hfpd", 0);
if (priv->vl_hfpd == 0) {
debug("Can't get right margin\n");
return -ENXIO;
}
priv->vl_hbpd = (u_char)fdtdec_get_int(blob, node,
"samsung,vl-hbpd", 0);
if (priv->vl_hbpd == 0) {
debug("Can't get left margin\n");
return -ENXIO;
}
priv->vl_vspw = (u_char)fdtdec_get_int(blob, node,
"samsung,vl-vspw", 0);
if (priv->vl_vspw == 0) {
debug("Can't get vsync width\n");
return -ENXIO;
}
priv->vl_vfpd = fdtdec_get_int(blob, node,
"samsung,vl-vfpd", 0);
if (priv->vl_vfpd == 0) {
debug("Can't get lower margin\n");
return -ENXIO;
}
priv->vl_vbpd = fdtdec_get_int(blob, node, "samsung,vl-vbpd", 0);
if (priv->vl_vbpd == 0) {
debug("Can't get upper margin\n");
return -ENXIO;
}
priv->vl_cmd_allow_len = fdtdec_get_int(blob, node,
"samsung,vl-cmd-allow-len", 0);
priv->win_id = fdtdec_get_int(blob, node, "samsung,winid", 0);
priv->init_delay = fdtdec_get_int(blob, node,
"samsung,init-delay", 0);
priv->power_on_delay = fdtdec_get_int(blob, node,
"samsung,power-on-delay", 0);
priv->reset_delay = fdtdec_get_int(blob, node,
"samsung,reset-delay", 0);
priv->interface_mode = fdtdec_get_int(blob, node,
"samsung,interface-mode", 0);
priv->mipi_enabled = fdtdec_get_int(blob, node,
"samsung,mipi-enabled", 0);
priv->dp_enabled = fdtdec_get_int(blob, node,
"samsung,dp-enabled", 0);
priv->cs_setup = fdtdec_get_int(blob, node,
"samsung,cs-setup", 0);
priv->wr_setup = fdtdec_get_int(blob, node,
"samsung,wr-setup", 0);
priv->wr_act = fdtdec_get_int(blob, node, "samsung,wr-act", 0);
priv->wr_hold = fdtdec_get_int(blob, node, "samsung,wr-hold", 0);
priv->logo_on = fdtdec_get_int(blob, node, "samsung,logo-on", 0);
if (priv->logo_on) {
priv->logo_width = fdtdec_get_int(blob, node,
"samsung,logo-width", 0);
priv->logo_height = fdtdec_get_int(blob, node,
"samsung,logo-height", 0);
priv->logo_addr = fdtdec_get_int(blob, node,
"samsung,logo-addr", 0);
}
priv->rgb_mode = fdtdec_get_int(blob, node,
"samsung,rgb-mode", 0);
priv->pclk_name = fdtdec_get_int(blob, node,
"samsung,pclk-name", 0);
priv->sclk_div = fdtdec_get_int(blob, node,
"samsung,sclk-div", 0);
priv->dual_lcd_enabled = fdtdec_get_int(blob, node,
"samsung,dual-lcd-enabled", 0);
return 0;
}
static int exynos_fb_probe(struct udevice *dev)
{
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct exynos_fb_priv *priv = dev_get_priv(dev);
struct udevice *panel, *bridge;
struct udevice *dp;
int ret;
debug("%s: start\n", __func__);
set_system_display_ctrl();
set_lcd_clk();
#ifdef CONFIG_EXYNOS_MIPI_DSIM
exynos_init_dsim_platform_data(&panel_info);
#endif
exynos_fimd_lcd_init(dev);
ret = uclass_first_device(UCLASS_PANEL, &panel);
if (ret) {
printf("LCD panel failed to probe\n");
return ret;
}
if (!panel) {
printf("LCD panel not found\n");
return -ENODEV;
}
ret = uclass_first_device(UCLASS_DISPLAY, &dp);
if (ret) {
debug("%s: Display device error %d\n", __func__, ret);
return ret;
}
if (!dev) {
debug("%s: Display device missing\n", __func__);
return -ENODEV;
}
ret = display_enable(dp, 18, NULL);
if (ret) {
debug("%s: Display enable error %d\n", __func__, ret);
return ret;
}
/* backlight / pwm */
ret = panel_enable_backlight(panel);
if (ret) {
debug("%s: backlight error: %d\n", __func__, ret);
return ret;
}
ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
if (!ret)
ret = video_bridge_set_backlight(bridge, 80);
if (ret) {
debug("%s: No video bridge, or no backlight on bridge\n",
__func__);
exynos_pinmux_config(PERIPH_ID_PWM0, 0);
}
uc_priv->xsize = priv->vl_col;
uc_priv->ysize = priv->vl_row;
uc_priv->bpix = priv->vl_bpix;
/* Enable flushing after LCD writes if requested */
video_set_flush_dcache(dev, true);
return 0;
}
static int exynos_fb_bind(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
/* This is the maximum panel size we expect to see */
plat->size = 1920 * 1080 * 2;
return 0;
}
static const struct video_ops exynos_fb_ops = {
};
static const struct udevice_id exynos_fb_ids[] = {
{ .compatible = "samsung,exynos-fimd" },
{ }
};
U_BOOT_DRIVER(exynos_fb) = {
.name = "exynos_fb",
.id = UCLASS_VIDEO,
.of_match = exynos_fb_ids,
.ops = &exynos_fb_ops,
.bind = exynos_fb_bind,
.probe = exynos_fb_probe,
.ofdata_to_platdata = exynos_fb_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct exynos_fb_priv),
};
@@ -0,0 +1,323 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <common.h>
#include <malloc.h>
#include <fdtdec.h>
#include <linux/libfdt.h>
#include <linux/compat.h>
#include <linux/err.h>
#include <asm/arch/dsim.h>
#include <asm/arch/mipi_dsim.h>
#include <asm/arch/power.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clk.h>
#include "exynos_mipi_dsi_lowlevel.h"
#include "exynos_mipi_dsi_common.h"
#define master_to_driver(a) (a->dsim_lcd_drv)
#define master_to_device(a) (a->dsim_lcd_dev)
DECLARE_GLOBAL_DATA_PTR;
struct mipi_dsim_ddi {
int bus_id;
struct list_head list;
struct mipi_dsim_lcd_device *dsim_lcd_dev;
struct mipi_dsim_lcd_driver *dsim_lcd_drv;
};
static LIST_HEAD(dsim_ddi_list);
static LIST_HEAD(dsim_lcd_dev_list);
int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev)
{
struct mipi_dsim_ddi *dsim_ddi;
if (!lcd_dev) {
debug("mipi_dsim_lcd_device is NULL.\n");
return -EFAULT;
}
if (!lcd_dev->name) {
debug("dsim_lcd_device name is NULL.\n");
return -EFAULT;
}
dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL);
if (!dsim_ddi) {
debug("failed to allocate dsim_ddi object.\n");
return -EFAULT;
}
dsim_ddi->dsim_lcd_dev = lcd_dev;
list_add_tail(&dsim_ddi->list, &dsim_ddi_list);
return 0;
}
struct mipi_dsim_ddi
*exynos_mipi_dsi_find_lcd_device(struct mipi_dsim_lcd_driver *lcd_drv)
{
struct mipi_dsim_ddi *dsim_ddi;
struct mipi_dsim_lcd_device *lcd_dev;
list_for_each_entry(dsim_ddi, &dsim_ddi_list, list) {
lcd_dev = dsim_ddi->dsim_lcd_dev;
if (!lcd_dev)
continue;
if (lcd_drv->id >= 0) {
if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0 &&
lcd_drv->id == lcd_dev->id) {
/**
* bus_id would be used to identify
* connected bus.
*/
dsim_ddi->bus_id = lcd_dev->bus_id;
return dsim_ddi;
}
} else {
if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) {
/**
* bus_id would be used to identify
* connected bus.
*/
dsim_ddi->bus_id = lcd_dev->bus_id;
return dsim_ddi;
}
}
kfree(dsim_ddi);
list_del(&dsim_ddi_list);
}
return NULL;
}
int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv)
{
struct mipi_dsim_ddi *dsim_ddi;
if (!lcd_drv) {
debug("mipi_dsim_lcd_driver is NULL.\n");
return -EFAULT;
}
if (!lcd_drv->name) {
debug("dsim_lcd_driver name is NULL.\n");
return -EFAULT;
}
dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv);
if (!dsim_ddi) {
debug("mipi_dsim_ddi object not found.\n");
return -EFAULT;
}
dsim_ddi->dsim_lcd_drv = lcd_drv;
debug("registered panel driver(%s) to mipi-dsi driver.\n",
lcd_drv->name);
return 0;
}
struct mipi_dsim_ddi
*exynos_mipi_dsi_bind_lcd_ddi(struct mipi_dsim_device *dsim,
const char *name)
{
struct mipi_dsim_ddi *dsim_ddi;
struct mipi_dsim_lcd_driver *lcd_drv;
struct mipi_dsim_lcd_device *lcd_dev;
list_for_each_entry(dsim_ddi, &dsim_ddi_list, list) {
lcd_drv = dsim_ddi->dsim_lcd_drv;
lcd_dev = dsim_ddi->dsim_lcd_dev;
if (!lcd_drv || !lcd_dev)
continue;
debug("lcd_drv->id = %d, lcd_dev->id = %d\n",
lcd_drv->id, lcd_dev->id);
if ((strcmp(lcd_drv->name, name) == 0)) {
lcd_dev->master = dsim;
dsim->dsim_lcd_dev = lcd_dev;
dsim->dsim_lcd_drv = lcd_drv;
return dsim_ddi;
}
}
return NULL;
}
/* define MIPI-DSI Master operations. */
static struct mipi_dsim_master_ops master_ops = {
.cmd_write = exynos_mipi_dsi_wr_data,
.get_dsim_frame_done = exynos_mipi_dsi_get_frame_done_status,
.clear_dsim_frame_done = exynos_mipi_dsi_clear_frame_done,
};
int exynos_mipi_dsi_init(struct exynos_platform_mipi_dsim *dsim_pd)
{
struct mipi_dsim_device *dsim;
struct mipi_dsim_config *dsim_config;
struct mipi_dsim_ddi *dsim_ddi;
dsim = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL);
if (!dsim) {
debug("failed to allocate dsim object.\n");
return -EFAULT;
}
/* get mipi_dsim_config. */
dsim_config = dsim_pd->dsim_config;
if (dsim_config == NULL) {
debug("failed to get dsim config data.\n");
return -EFAULT;
}
dsim->pd = dsim_pd;
dsim->dsim_config = dsim_config;
dsim->master_ops = &master_ops;
/* bind lcd ddi matched with panel name. */
dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name);
if (!dsim_ddi) {
debug("mipi_dsim_ddi object not found.\n");
return -ENOSYS;
}
if (dsim_pd->lcd_power)
dsim_pd->lcd_power();
if (dsim_pd->mipi_power)
dsim_pd->mipi_power();
/* phy_enable(unsigned int dev_index, unsigned int enable) */
if (dsim_pd->phy_enable)
dsim_pd->phy_enable(0, 1);
set_mipi_clk();
exynos_mipi_dsi_init_dsim(dsim);
exynos_mipi_dsi_init_link(dsim);
exynos_mipi_dsi_set_hs_enable(dsim);
/* set display timing. */
exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config);
/* initialize mipi-dsi client(lcd panel). */
if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->mipi_panel_init) {
dsim_ddi->dsim_lcd_drv->mipi_panel_init(dsim);
dsim_ddi->dsim_lcd_drv->mipi_display_on(dsim);
}
debug("mipi-dsi driver(%s mode) has been probed.\n",
(dsim_config->e_interface == DSIM_COMMAND) ?
"CPU" : "RGB");
return 0;
}
int exynos_dsim_config_parse_dt(const void *blob, struct mipi_dsim_config *dt,
struct mipi_dsim_lcd_device *lcd_dt)
{
int node;
node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS_MIPI_DSI);
if (node <= 0) {
printf("exynos_mipi_dsi: Can't get device node for mipi dsi\n");
return -ENODEV;
}
dt->e_interface = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-interface", 0);
dt->e_virtual_ch = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-virtual-ch", 0);
dt->e_pixel_format = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-pixel-format", 0);
dt->e_burst_mode = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-burst-mode", 0);
dt->e_no_data_lane = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-no-data-lane", 0);
dt->e_byte_clk = fdtdec_get_int(blob, node,
"samsung,dsim-config-e-byte-clk", 0);
dt->hfp = fdtdec_get_int(blob, node,
"samsung,dsim-config-hfp", 0);
dt->p = fdtdec_get_int(blob, node,
"samsung,dsim-config-p", 0);
dt->m = fdtdec_get_int(blob, node,
"samsung,dsim-config-m", 0);
dt->s = fdtdec_get_int(blob, node,
"samsung,dsim-config-s", 0);
dt->pll_stable_time = fdtdec_get_int(blob, node,
"samsung,dsim-config-pll-stable-time", 0);
dt->esc_clk = fdtdec_get_int(blob, node,
"samsung,dsim-config-esc-clk", 0);
dt->stop_holding_cnt = fdtdec_get_int(blob, node,
"samsung,dsim-config-stop-holding-cnt", 0);
dt->bta_timeout = fdtdec_get_int(blob, node,
"samsung,dsim-config-bta-timeout", 0);
dt->rx_timeout = fdtdec_get_int(blob, node,
"samsung,dsim-config-rx-timeout", 0);
lcd_dt->name = fdtdec_get_config_string(blob,
"samsung,dsim-device-name");
lcd_dt->id = fdtdec_get_int(blob, node,
"samsung,dsim-device-id", 0);
lcd_dt->bus_id = fdtdec_get_int(blob, node,
"samsung,dsim-device-bus_id", 0);
lcd_dt->reverse_panel = fdtdec_get_int(blob, node,
"samsung,dsim-device-reverse-panel", 0);
return 0;
}
void exynos_init_dsim_platform_data(vidinfo_t *vid)
{
static struct mipi_dsim_config dsim_config_dt;
static struct exynos_platform_mipi_dsim dsim_platform_data_dt;
static struct mipi_dsim_lcd_device mipi_lcd_device_dt;
if (exynos_dsim_config_parse_dt(gd->fdt_blob, &dsim_config_dt,
&mipi_lcd_device_dt))
debug("Can't get proper dsim config.\n");
strcpy(dsim_platform_data_dt.lcd_panel_name, mipi_lcd_device_dt.name);
dsim_platform_data_dt.dsim_config = &dsim_config_dt;
dsim_platform_data_dt.mipi_power = mipi_power;
dsim_platform_data_dt.phy_enable = set_mipi_phy_ctrl;
dsim_platform_data_dt.lcd_panel_info = (void *)vid;
mipi_lcd_device_dt.platform_data = (void *)&dsim_platform_data_dt;
exynos_mipi_dsi_register_lcd_device(&mipi_lcd_device_dt);
vid->dsim_platform_data_dt = &dsim_platform_data_dt;
}
@@ -0,0 +1,619 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <common.h>
#include <lcd.h>
#include <linux/err.h>
#include <asm/arch/dsim.h>
#include <asm/arch/mipi_dsim.h>
#include "exynos_mipi_dsi_lowlevel.h"
#define MHZ (1000 * 1000)
#define FIN_HZ (24 * MHZ)
#define DFIN_PLL_MIN_HZ (6 * MHZ)
#define DFIN_PLL_MAX_HZ (12 * MHZ)
#define DFVCO_MIN_HZ (500 * MHZ)
#define DFVCO_MAX_HZ (1000 * MHZ)
#define TRY_GET_FIFO_TIMEOUT (5000 * 2)
/* MIPI-DSIM status types. */
enum {
DSIM_STATE_INIT, /* should be initialized. */
DSIM_STATE_STOP, /* CPU and LCDC are LP mode. */
DSIM_STATE_HSCLKEN, /* HS clock was enabled. */
DSIM_STATE_ULPS
};
/* define DSI lane types. */
enum {
DSIM_LANE_CLOCK = (1 << 0),
DSIM_LANE_DATA0 = (1 << 1),
DSIM_LANE_DATA1 = (1 << 2),
DSIM_LANE_DATA2 = (1 << 3),
DSIM_LANE_DATA3 = (1 << 4)
};
static unsigned int dpll_table[15] = {
100, 120, 170, 220, 270,
320, 390, 450, 510, 560,
640, 690, 770, 870, 950
};
static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim,
const unsigned char *data0, unsigned int data1)
{
unsigned int data_cnt = 0, payload = 0;
/* in case that data count is more then 4 */
for (data_cnt = 0; data_cnt < data1; data_cnt += 4) {
/*
* after sending 4bytes per one time,
* send remainder data less then 4.
*/
if ((data1 - data_cnt) < 4) {
if ((data1 - data_cnt) == 3) {
payload = data0[data_cnt] |
data0[data_cnt + 1] << 8 |
data0[data_cnt + 2] << 16;
debug("count = 3 payload = %x, %x %x %x\n",
payload, data0[data_cnt],
data0[data_cnt + 1],
data0[data_cnt + 2]);
} else if ((data1 - data_cnt) == 2) {
payload = data0[data_cnt] |
data0[data_cnt + 1] << 8;
debug("count = 2 payload = %x, %x %x\n", payload,
data0[data_cnt], data0[data_cnt + 1]);
} else if ((data1 - data_cnt) == 1) {
payload = data0[data_cnt];
}
} else {
/* send 4bytes per one time. */
payload = data0[data_cnt] |
data0[data_cnt + 1] << 8 |
data0[data_cnt + 2] << 16 |
data0[data_cnt + 3] << 24;
debug("count = 4 payload = %x, %x %x %x %x\n",
payload, *(u8 *)(data0 + data_cnt),
data0[data_cnt + 1],
data0[data_cnt + 2],
data0[data_cnt + 3]);
}
exynos_mipi_dsi_wr_tx_data(dsim, payload);
}
}
int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
const unsigned char *data0, unsigned int data1)
{
unsigned int timeout = TRY_GET_FIFO_TIMEOUT;
unsigned long delay_val, delay;
unsigned int check_rx_ack = 0;
if (dsim->state == DSIM_STATE_ULPS) {
debug("state is ULPS.\n");
return -EINVAL;
}
delay_val = MHZ / dsim->dsim_config->esc_clk;
delay = 10 * delay_val;
mdelay(delay);
/* only if transfer mode is LPDT, wait SFR becomes empty. */
if (dsim->state == DSIM_STATE_STOP) {
while (!(exynos_mipi_dsi_get_fifo_state(dsim) &
SFR_HEADER_EMPTY)) {
if ((timeout--) > 0)
mdelay(1);
else {
debug("SRF header fifo is not empty.\n");
return -EINVAL;
}
}
}
switch (data_id) {
/* short packet types of packet types for command. */
case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
case MIPI_DSI_DCS_SHORT_WRITE:
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
debug("data0 = %x data1 = %x\n",
data0[0], data0[1]);
exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
if (check_rx_ack) {
/* process response func should be implemented */
return 0;
} else {
return -EINVAL;
}
/* general command */
case MIPI_DSI_COLOR_MODE_OFF:
case MIPI_DSI_COLOR_MODE_ON:
case MIPI_DSI_SHUTDOWN_PERIPHERAL:
case MIPI_DSI_TURN_ON_PERIPHERAL:
exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
if (check_rx_ack) {
/* process response func should be implemented. */
return 0;
} else {
return -EINVAL;
}
/* packet types for video data */
case MIPI_DSI_V_SYNC_START:
case MIPI_DSI_V_SYNC_END:
case MIPI_DSI_H_SYNC_START:
case MIPI_DSI_H_SYNC_END:
case MIPI_DSI_END_OF_TRANSMISSION:
return 0;
/* short and response packet types for command */
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
case MIPI_DSI_DCS_READ:
exynos_mipi_dsi_clear_all_interrupt(dsim);
exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
/* process response func should be implemented. */
return 0;
/* long packet type and null packet */
case MIPI_DSI_NULL_PACKET:
case MIPI_DSI_BLANKING_PACKET:
return 0;
case MIPI_DSI_GENERIC_LONG_WRITE:
case MIPI_DSI_DCS_LONG_WRITE:
{
unsigned int payload = 0;
/* if data count is less then 4, then send 3bytes data. */
if (data1 < 4) {
payload = data0[0] |
data0[1] << 8 |
data0[2] << 16;
exynos_mipi_dsi_wr_tx_data(dsim, payload);
debug("count = %d payload = %x,%x %x %x\n",
data1, payload, data0[0],
data0[1], data0[2]);
} else {
/* in case that data count is more then 4 */
exynos_mipi_dsi_long_data_wr(dsim, data0, data1);
}
/* put data into header fifo */
exynos_mipi_dsi_wr_tx_header(dsim, data_id, data1 & 0xff,
(data1 & 0xff00) >> 8);
}
if (check_rx_ack)
/* process response func should be implemented. */
return 0;
else
return -EINVAL;
/* packet typo for video data */
case MIPI_DSI_PACKED_PIXEL_STREAM_16:
case MIPI_DSI_PACKED_PIXEL_STREAM_18:
case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
case MIPI_DSI_PACKED_PIXEL_STREAM_24:
if (check_rx_ack) {
/* process response func should be implemented. */
return 0;
} else {
return -EINVAL;
}
default:
debug("data id %x is not supported current DSI spec.\n",
data_id);
return -EINVAL;
}
return 0;
}
int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, unsigned int enable)
{
int sw_timeout;
if (enable) {
sw_timeout = 1000;
exynos_mipi_dsi_clear_interrupt(dsim);
exynos_mipi_dsi_enable_pll(dsim, 1);
while (1) {
sw_timeout--;
if (exynos_mipi_dsi_is_pll_stable(dsim))
return 0;
if (sw_timeout == 0)
return -EINVAL;
}
} else
exynos_mipi_dsi_enable_pll(dsim, 0);
return 0;
}
unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim,
unsigned int pre_divider, unsigned int main_divider,
unsigned int scaler)
{
unsigned long dfin_pll, dfvco, dpll_out;
unsigned int i, freq_band = 0xf;
dfin_pll = (FIN_HZ / pre_divider);
/******************************************************
* Serial Clock(=ByteClk X 8) FreqBand[3:0] *
******************************************************
* ~ 99.99 MHz 0000
* 100 ~ 119.99 MHz 0001
* 120 ~ 159.99 MHz 0010
* 160 ~ 199.99 MHz 0011
* 200 ~ 239.99 MHz 0100
* 140 ~ 319.99 MHz 0101
* 320 ~ 389.99 MHz 0110
* 390 ~ 449.99 MHz 0111
* 450 ~ 509.99 MHz 1000
* 510 ~ 559.99 MHz 1001
* 560 ~ 639.99 MHz 1010
* 640 ~ 689.99 MHz 1011
* 690 ~ 769.99 MHz 1100
* 770 ~ 869.99 MHz 1101
* 870 ~ 949.99 MHz 1110
* 950 ~ 1000 MHz 1111
******************************************************/
if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) {
debug("fin_pll range should be 6MHz ~ 12MHz\n");
exynos_mipi_dsi_enable_afc(dsim, 0, 0);
} else {
if (dfin_pll < 7 * MHZ)
exynos_mipi_dsi_enable_afc(dsim, 1, 0x1);
else if (dfin_pll < 8 * MHZ)
exynos_mipi_dsi_enable_afc(dsim, 1, 0x0);
else if (dfin_pll < 9 * MHZ)
exynos_mipi_dsi_enable_afc(dsim, 1, 0x3);
else if (dfin_pll < 10 * MHZ)
exynos_mipi_dsi_enable_afc(dsim, 1, 0x2);
else if (dfin_pll < 11 * MHZ)
exynos_mipi_dsi_enable_afc(dsim, 1, 0x5);
else
exynos_mipi_dsi_enable_afc(dsim, 1, 0x4);
}
dfvco = dfin_pll * main_divider;
debug("dfvco = %lu, dfin_pll = %lu, main_divider = %d\n",
dfvco, dfin_pll, main_divider);
if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ)
debug("fvco range should be 500MHz ~ 1000MHz\n");
dpll_out = dfvco / (1 << scaler);
debug("dpll_out = %lu, dfvco = %lu, scaler = %d\n",
dpll_out, dfvco, scaler);
for (i = 0; i < ARRAY_SIZE(dpll_table); i++) {
if (dpll_out < dpll_table[i] * MHZ) {
freq_band = i;
break;
}
}
debug("freq_band = %d\n", freq_band);
exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler);
exynos_mipi_dsi_hs_zero_ctrl(dsim, 0);
exynos_mipi_dsi_prep_ctrl(dsim, 0);
/* Freq Band */
exynos_mipi_dsi_pll_freq_band(dsim, freq_band);
/* Stable time */
exynos_mipi_dsi_pll_stable_time(dsim,
dsim->dsim_config->pll_stable_time);
/* Enable PLL */
debug("FOUT of mipi dphy pll is %luMHz\n",
(dpll_out / MHZ));
return dpll_out;
}
int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim,
unsigned int byte_clk_sel, unsigned int enable)
{
unsigned int esc_div;
unsigned long esc_clk_error_rate;
unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0;
if (enable) {
dsim->e_clk_src = byte_clk_sel;
/* Escape mode clock and byte clock source */
exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel);
/* DPHY, DSIM Link : D-PHY clock out */
if (byte_clk_sel == DSIM_PLL_OUT_DIV8) {
hs_clk = exynos_mipi_dsi_change_pll(dsim,
dsim->dsim_config->p, dsim->dsim_config->m,
dsim->dsim_config->s);
if (hs_clk == 0) {
debug("failed to get hs clock.\n");
return -EINVAL;
}
byte_clk = hs_clk / 8;
exynos_mipi_dsi_enable_pll_bypass(dsim, 0);
exynos_mipi_dsi_pll_on(dsim, 1);
/* DPHY : D-PHY clock out, DSIM link : external clock out */
} else if (byte_clk_sel == DSIM_EXT_CLK_DIV8)
debug("not support EXT CLK source for MIPI DSIM\n");
else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS)
debug("not support EXT CLK source for MIPI DSIM\n");
/* escape clock divider */
esc_div = byte_clk / (dsim->dsim_config->esc_clk);
debug("esc_div = %d, byte_clk = %lu, esc_clk = %lu\n",
esc_div, byte_clk, dsim->dsim_config->esc_clk);
if ((byte_clk / esc_div) >= (20 * MHZ) ||
(byte_clk / esc_div) > dsim->dsim_config->esc_clk)
esc_div += 1;
escape_clk = byte_clk / esc_div;
debug("escape_clk = %lu, byte_clk = %lu, esc_div = %d\n",
escape_clk, byte_clk, esc_div);
/* enable escape clock. */
exynos_mipi_dsi_enable_byte_clock(dsim, 1);
/* enable byte clk and escape clock */
exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div);
/* escape clock on lane */
exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
(DSIM_LANE_CLOCK | dsim->data_lane), 1);
debug("byte clock is %luMHz\n",
(byte_clk / MHZ));
debug("escape clock that user's need is %lu\n",
(dsim->dsim_config->esc_clk / MHZ));
debug("escape clock divider is %x\n", esc_div);
debug("escape clock is %luMHz\n",
((byte_clk / esc_div) / MHZ));
if ((byte_clk / esc_div) > escape_clk) {
esc_clk_error_rate = escape_clk /
(byte_clk / esc_div);
debug("error rate is %lu over.\n",
(esc_clk_error_rate / 100));
} else if ((byte_clk / esc_div) < (escape_clk)) {
esc_clk_error_rate = (byte_clk / esc_div) /
escape_clk;
debug("error rate is %lu under.\n",
(esc_clk_error_rate / 100));
}
} else {
exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
(DSIM_LANE_CLOCK | dsim->data_lane), 0);
exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0);
/* disable escape clock. */
exynos_mipi_dsi_enable_byte_clock(dsim, 0);
if (byte_clk_sel == DSIM_PLL_OUT_DIV8)
exynos_mipi_dsi_pll_on(dsim, 0);
}
return 0;
}
int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim)
{
dsim->state = DSIM_STATE_INIT;
switch (dsim->dsim_config->e_no_data_lane) {
case DSIM_DATA_LANE_1:
dsim->data_lane = DSIM_LANE_DATA0;
break;
case DSIM_DATA_LANE_2:
dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1;
break;
case DSIM_DATA_LANE_3:
dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
DSIM_LANE_DATA2;
break;
case DSIM_DATA_LANE_4:
dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
DSIM_LANE_DATA2 | DSIM_LANE_DATA3;
break;
default:
debug("data lane is invalid.\n");
return -EINVAL;
};
exynos_mipi_dsi_sw_reset(dsim);
exynos_mipi_dsi_dp_dn_swap(dsim, 0);
return 0;
}
int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
unsigned int enable)
{
/* enable only frame done interrupt */
exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable);
return 0;
}
static void convert_to_fb_videomode(struct fb_videomode *mode1,
struct vidinfo *mode2)
{
mode1->xres = mode2->vl_width;
mode1->yres = mode2->vl_height;
mode1->upper_margin = mode2->vl_vfpd;
mode1->lower_margin = mode2->vl_vbpd;
mode1->left_margin = mode2->vl_hfpd;
mode1->right_margin = mode2->vl_hbpd;
mode1->vsync_len = mode2->vl_vspw;
mode1->hsync_len = mode2->vl_hspw;
}
int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
struct mipi_dsim_config *dsim_config)
{
struct exynos_platform_mipi_dsim *dsim_pd;
struct fb_videomode lcd_video;
struct vidinfo *vid;
dsim_pd = (struct exynos_platform_mipi_dsim *)dsim->pd;
vid = (struct vidinfo *)dsim_pd->lcd_panel_info;
convert_to_fb_videomode(&lcd_video, vid);
/* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */
if (dsim->dsim_config->e_interface == (u32) DSIM_VIDEO) {
if (dsim->dsim_config->auto_vertical_cnt == 0) {
exynos_mipi_dsi_set_main_disp_vporch(dsim,
vid->vl_cmd_allow_len,
lcd_video.upper_margin,
lcd_video.lower_margin);
exynos_mipi_dsi_set_main_disp_hporch(dsim,
lcd_video.left_margin,
lcd_video.right_margin);
exynos_mipi_dsi_set_main_disp_sync_area(dsim,
lcd_video.vsync_len,
lcd_video.hsync_len);
}
}
exynos_mipi_dsi_set_main_disp_resol(dsim, lcd_video.xres,
lcd_video.yres);
exynos_mipi_dsi_display_config(dsim, dsim->dsim_config);
debug("lcd panel ==> width = %d, height = %d\n",
lcd_video.xres, lcd_video.yres);
return 0;
}
int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim)
{
unsigned int time_out = 100;
switch (dsim->state) {
case DSIM_STATE_INIT:
exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f);
/* dsi configuration */
exynos_mipi_dsi_init_config(dsim);
exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1);
exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1);
/* set clock configuration */
exynos_mipi_dsi_set_clock(dsim,
dsim->dsim_config->e_byte_clk, 1);
/* check clock and data lane state are stop state */
while (!(exynos_mipi_dsi_is_lane_state(dsim))) {
time_out--;
if (time_out == 0) {
debug("DSI Master is not stop state.\n");
debug("Check initialization process\n");
return -EINVAL;
}
}
dsim->state = DSIM_STATE_STOP;
/* BTA sequence counters */
exynos_mipi_dsi_set_stop_state_counter(dsim,
dsim->dsim_config->stop_holding_cnt);
exynos_mipi_dsi_set_bta_timeout(dsim,
dsim->dsim_config->bta_timeout);
exynos_mipi_dsi_set_lpdr_timeout(dsim,
dsim->dsim_config->rx_timeout);
return 0;
default:
debug("DSI Master is already init.\n");
return 0;
}
return 0;
}
int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim)
{
if (dsim->state == DSIM_STATE_STOP) {
if (dsim->e_clk_src != DSIM_EXT_CLK_BYPASS) {
dsim->state = DSIM_STATE_HSCLKEN;
/* set LCDC and CPU transfer mode to HS. */
exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
exynos_mipi_dsi_enable_hs_clock(dsim, 1);
return 0;
} else
debug("clock source is external bypass.\n");
} else
debug("DSIM is not stop state.\n");
return 0;
}
int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int mode)
{
if (mode) {
if (dsim->state != DSIM_STATE_HSCLKEN) {
debug("HS Clock lane is not enabled.\n");
return -EINVAL;
}
exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
} else {
if (dsim->state == DSIM_STATE_INIT || dsim->state ==
DSIM_STATE_ULPS) {
debug("DSI Master is not STOP or HSDT state.\n");
return -EINVAL;
}
exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
}
return 0;
}
int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim)
{
return _exynos_mipi_dsi_get_frame_done_status(dsim);
}
int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
{
_exynos_mipi_dsi_clear_frame_done(dsim);
return 0;
}
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <linux/fb.h>
#ifndef _EXYNOS_MIPI_DSI_COMMON_H
#define _EXYNOS_MIPI_DSI_COMMON_H
int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
const unsigned char *data0, unsigned int data1);
int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, unsigned int enable);
unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim,
unsigned int pre_divider, unsigned int main_divider,
unsigned int scaler);
int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim,
unsigned int byte_clk_sel, unsigned int enable);
int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim);
int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
struct mipi_dsim_config *dsim_info);
int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim);
int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim);
int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int mode);
int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
unsigned int enable);
int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim);
int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
#endif /* _EXYNOS_MIPI_DSI_COMMON_H */
@@ -0,0 +1,638 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <common.h>
#include <asm/arch/dsim.h>
#include <asm/arch/mipi_dsim.h>
#include <asm/arch/power.h>
#include <asm/arch/cpu.h>
#include "exynos_mipi_dsi_lowlevel.h"
#include "exynos_mipi_dsi_common.h"
void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = readl(&mipi_dsim->swrst);
reg |= DSIM_FUNCRST;
writel(reg, &mipi_dsim->swrst);
}
void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim)
{
unsigned int reg = 0;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = readl(&mipi_dsim->swrst);
reg |= DSIM_SWRST;
reg |= DSIM_FUNCRST;
writel(reg, &mipi_dsim->swrst);
}
void exynos_mipi_dsi_sw_release(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->intsrc);
reg |= INTSRC_SWRST_RELEASE;
writel(reg, &mipi_dsim->intsrc);
}
void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
unsigned int mode, unsigned int mask)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->intmsk);
if (mask)
reg |= mode;
else
reg &= ~mode;
writel(reg, &mipi_dsim->intmsk);
}
void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
unsigned int cfg)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = readl(&mipi_dsim->fifoctrl);
writel(reg & ~(cfg), &mipi_dsim->fifoctrl);
udelay(10 * 1000);
reg |= cfg;
writel(reg, &mipi_dsim->fifoctrl);
}
/*
* this function set PLL P, M and S value in D-PHY
*/
void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
unsigned int value)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
writel(DSIM_AFC_CTL(value), &mipi_dsim->phyacchr);
}
void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
unsigned int width_resol, unsigned int height_resol)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
/* standby should be set after configuration so set to not ready*/
reg = (readl(&mipi_dsim->mdresol)) & ~(DSIM_MAIN_STAND_BY);
writel(reg, &mipi_dsim->mdresol);
/* reset resolution */
reg &= ~(DSIM_MAIN_VRESOL(0x7ff) | DSIM_MAIN_HRESOL(0x7ff));
reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol);
reg |= DSIM_MAIN_STAND_BY;
writel(reg, &mipi_dsim->mdresol);
}
void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
unsigned int cmd_allow, unsigned int vfront, unsigned int vback)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = (readl(&mipi_dsim->mvporch)) &
~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) |
(DSIM_MAIN_VBP_MASK));
reg |= ((cmd_allow & 0xf) << DSIM_CMD_ALLOW_SHIFT) |
((vfront & 0x7ff) << DSIM_STABLE_VFP_SHIFT) |
((vback & 0x7ff) << DSIM_MAIN_VBP_SHIFT);
writel(reg, &mipi_dsim->mvporch);
}
void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
unsigned int front, unsigned int back)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = (readl(&mipi_dsim->mhporch)) &
~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK));
reg |= (front << DSIM_MAIN_HFP_SHIFT) | (back << DSIM_MAIN_HBP_SHIFT);
writel(reg, &mipi_dsim->mhporch);
}
void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
unsigned int vert, unsigned int hori)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = (readl(&mipi_dsim->msync)) &
~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK));
reg |= ((vert & 0x3ff) << DSIM_MAIN_VSA_SHIFT) |
(hori << DSIM_MAIN_HSA_SHIFT);
writel(reg, &mipi_dsim->msync);
}
void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
unsigned int vert, unsigned int hori)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = (readl(&mipi_dsim->sdresol)) &
~(DSIM_SUB_STANDY_MASK);
writel(reg, &mipi_dsim->sdresol);
reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK);
reg |= ((vert & 0x7ff) << DSIM_SUB_VRESOL_SHIFT) |
((hori & 0x7ff) << DSIM_SUB_HRESOL_SHIFT);
writel(reg, &mipi_dsim->sdresol);
/* DSIM STANDBY */
reg |= (1 << DSIM_SUB_STANDY_SHIFT);
writel(reg, &mipi_dsim->sdresol);
}
void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim)
{
struct mipi_dsim_config *dsim_config = dsim->dsim_config;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int cfg = (readl(&mipi_dsim->config)) &
~((1 << DSIM_EOT_PACKET_SHIFT) |
(0x1f << DSIM_HSA_MODE_SHIFT) |
(0x3 << DSIM_NUM_OF_DATALANE_SHIFT));
cfg |= (dsim_config->auto_flush << DSIM_AUTO_FLUSH_SHIFT) |
(dsim_config->eot_disable << DSIM_EOT_PACKET_SHIFT) |
(dsim_config->auto_vertical_cnt << DSIM_AUTO_MODE_SHIFT) |
(dsim_config->hse << DSIM_HSE_MODE_SHIFT) |
(dsim_config->hfp << DSIM_HFP_MODE_SHIFT) |
(dsim_config->hbp << DSIM_HBP_MODE_SHIFT) |
(dsim_config->hsa << DSIM_HSA_MODE_SHIFT) |
(dsim_config->e_no_data_lane << DSIM_NUM_OF_DATALANE_SHIFT);
writel(cfg, &mipi_dsim->config);
}
void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
struct mipi_dsim_config *dsim_config)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
u32 reg = (readl(&mipi_dsim->config)) &
~((0x3 << DSIM_BURST_MODE_SHIFT) | (1 << DSIM_VIDEO_MODE_SHIFT)
| (0x3 << DSIM_MAINVC_SHIFT) | (0x7 << DSIM_MAINPIX_SHIFT)
| (0x3 << DSIM_SUBVC_SHIFT) | (0x7 << DSIM_SUBPIX_SHIFT));
if (dsim_config->e_interface == DSIM_VIDEO)
reg |= (1 << DSIM_VIDEO_MODE_SHIFT);
else if (dsim_config->e_interface == DSIM_COMMAND)
reg &= ~(1 << DSIM_VIDEO_MODE_SHIFT);
else {
printf("unknown lcd type.\n");
return;
}
/* main lcd */
reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << DSIM_BURST_MODE_SHIFT
| ((u8) (dsim_config->e_virtual_ch) & 0x3) << DSIM_MAINVC_SHIFT
| ((u8) (dsim_config->e_pixel_format) & 0x7) << DSIM_MAINPIX_SHIFT;
writel(reg, &mipi_dsim->config);
}
void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim,
unsigned int lane, unsigned int enable)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = readl(&mipi_dsim->config);
if (enable)
reg |= DSIM_LANE_ENx(lane);
else
reg &= ~DSIM_LANE_ENx(lane);
writel(reg, &mipi_dsim->config);
}
void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
unsigned int count)
{
unsigned int cfg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
/* get the data lane number. */
cfg = DSIM_NUM_OF_DATA_LANE(count);
writel(cfg, &mipi_dsim->config);
}
void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim,
unsigned int enable, unsigned int afc_code)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->phyacchr);
reg = 0;
if (enable) {
reg |= DSIM_AFC_EN;
reg &= ~(0x7 << DSIM_AFC_CTL_SHIFT);
reg |= DSIM_AFC_CTL(afc_code);
} else
reg &= ~DSIM_AFC_EN;
writel(reg, &mipi_dsim->phyacchr);
}
void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
~(DSIM_PLL_BYPASS_EXTERNAL);
reg |= enable << DSIM_PLL_BYPASS_SHIFT;
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
unsigned int freq_band)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
~(0x1f << DSIM_FREQ_BAND_SHIFT);
reg |= ((freq_band & 0x1f) << DSIM_FREQ_BAND_SHIFT);
writel(reg, &mipi_dsim->pllctrl);
}
void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
unsigned int pre_divider, unsigned int main_divider,
unsigned int scaler)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
~(0x7ffff << 1);
reg |= ((pre_divider & 0x3f) << DSIM_PREDIV_SHIFT) |
((main_divider & 0x1ff) << DSIM_MAIN_SHIFT) |
((scaler & 0x7) << DSIM_SCALER_SHIFT);
writel(reg, &mipi_dsim->pllctrl);
}
void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
unsigned int lock_time)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
writel(lock_time, &mipi_dsim->plltmr);
}
void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim,
unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
~(0x1 << DSIM_PLL_EN_SHIFT);
reg |= ((enable & 0x1) << DSIM_PLL_EN_SHIFT);
writel(reg, &mipi_dsim->pllctrl);
}
void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
unsigned int src)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
~(0x3 << DSIM_BYTE_CLK_SRC_SHIFT);
reg |= ((unsigned int) src) << DSIM_BYTE_CLK_SRC_SHIFT;
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
~(1 << DSIM_BYTE_CLKEN_SHIFT);
reg |= enable << DSIM_BYTE_CLKEN_SHIFT;
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
unsigned int enable, unsigned int prs_val)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
~((1 << DSIM_ESC_CLKEN_SHIFT) | (0xffff));
reg |= enable << DSIM_ESC_CLKEN_SHIFT;
if (enable)
reg |= prs_val;
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
unsigned int lane_sel, unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->clkctrl);
if (enable)
reg |= DSIM_LANE_ESC_CLKEN(lane_sel);
else
reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel);
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->escmode)) &
~(0x1 << DSIM_FORCE_STOP_STATE_SHIFT);
reg |= ((enable & 0x1) << DSIM_FORCE_STOP_STATE_SHIFT);
writel(reg, &mipi_dsim->escmode);
}
unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->status);
/**
* check clock and data lane states.
* if MIPI-DSI controller was enabled at bootloader then
* TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK.
* so it should be checked for two case.
*/
if ((reg & DSIM_STOP_STATE_DAT(0xf)) &&
((reg & DSIM_STOP_STATE_CLK) ||
(reg & DSIM_TX_READY_HS_CLK)))
return 1;
else
return 0;
}
void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
unsigned int cnt_val)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->escmode)) &
~(0x7ff << DSIM_STOP_STATE_CNT_SHIFT);
reg |= ((cnt_val & 0x7ff) << DSIM_STOP_STATE_CNT_SHIFT);
writel(reg, &mipi_dsim->escmode);
}
void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
unsigned int timeout)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->timeout)) &
~(0xff << DSIM_BTA_TOUT_SHIFT);
reg |= (timeout << DSIM_BTA_TOUT_SHIFT);
writel(reg, &mipi_dsim->timeout);
}
void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
unsigned int timeout)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->timeout)) &
~(0xffff << DSIM_LPDR_TOUT_SHIFT);
reg |= (timeout << DSIM_LPDR_TOUT_SHIFT);
writel(reg, &mipi_dsim->timeout);
}
void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int lp)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->escmode);
reg &= ~DSIM_CMD_LPDT_LP;
if (lp)
reg |= DSIM_CMD_LPDT_LP;
writel(reg, &mipi_dsim->escmode);
}
void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int lp)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->escmode);
reg &= ~DSIM_TX_LPDT_LP;
if (lp)
reg |= DSIM_TX_LPDT_LP;
writel(reg, &mipi_dsim->escmode);
}
void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
unsigned int enable)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->clkctrl)) &
~(1 << DSIM_TX_REQUEST_HSCLK_SHIFT);
reg |= enable << DSIM_TX_REQUEST_HSCLK_SHIFT;
writel(reg, &mipi_dsim->clkctrl);
}
void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
unsigned int swap_en)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->phyacchr1);
reg &= ~(0x3 << DSIM_DPDN_SWAP_DATA_SHIFT);
reg |= (swap_en & 0x3) << DSIM_DPDN_SWAP_DATA_SHIFT;
writel(reg, &mipi_dsim->phyacchr1);
}
void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
unsigned int hs_zero)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
~(0xf << DSIM_ZEROCTRL_SHIFT);
reg |= ((hs_zero & 0xf) << DSIM_ZEROCTRL_SHIFT);
writel(reg, &mipi_dsim->pllctrl);
}
void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (readl(&mipi_dsim->pllctrl)) &
~(0x7 << DSIM_PRECTRL_SHIFT);
reg |= ((prep & 0x7) << DSIM_PRECTRL_SHIFT);
writel(reg, &mipi_dsim->pllctrl);
}
void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->intsrc);
reg |= INTSRC_PLL_STABLE;
writel(reg, &mipi_dsim->intsrc);
}
void exynos_mipi_dsi_clear_all_interrupt(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
writel(0xffffffff, &mipi_dsim->intsrc);
}
unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim)
{
unsigned int reg;
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
reg = readl(&mipi_dsim->status);
return reg & DSIM_PLL_STABLE ? 1 : 0;
}
unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
return readl(&mipi_dsim->fifoctrl) & ~(0x1f);
}
void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim,
unsigned int di, const unsigned char data0, const unsigned char data1)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = (DSIM_PKTHDR_DAT1(data1) | DSIM_PKTHDR_DAT0(data0) |
DSIM_PKTHDR_DI(di));
writel(reg, &mipi_dsim->pkthdr);
}
unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device
*dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->intsrc);
return (reg & INTSRC_FRAME_DONE) ? 1 : 0;
}
void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
unsigned int reg = readl(&mipi_dsim->intsrc);
writel(reg | INTSRC_FRAME_DONE, &mipi_dsim->intsrc);
}
void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
unsigned int tx_data)
{
struct exynos_mipi_dsim *mipi_dsim =
(struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim();
writel(tx_data, &mipi_dsim->payload);
}
@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2012 Samsung Electronics
*
* Author: InKi Dae <inki.dae@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#ifndef _EXYNOS_MIPI_DSI_LOWLEVEL_H
#define _EXYNOS_MIPI_DSI_LOWLEVEL_H
void exynos_mipi_dsi_register(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_sw_release(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
unsigned int mode, unsigned int mask);
void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
unsigned int count);
void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
unsigned int cfg);
void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
unsigned int value);
void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
unsigned int value);
void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
unsigned int width_resol, unsigned int height_resol);
void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
unsigned int cmd_allow, unsigned int vfront, unsigned int vback);
void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
unsigned int front, unsigned int back);
void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
unsigned int vert, unsigned int hori);
void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
unsigned int vert, unsigned int hori);
void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
struct mipi_dsim_config *dsim_config);
void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
unsigned int count);
void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim,
unsigned int lane, unsigned int enable);
void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim,
unsigned int enable, unsigned int afc_code);
void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
unsigned int enable);
void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
unsigned int freq_band);
void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
unsigned int pre_divider, unsigned int main_divider,
unsigned int scaler);
void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
unsigned int lock_time);
void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim,
unsigned int enable);
void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
unsigned int src);
void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
unsigned int enable);
void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
unsigned int enable, unsigned int prs_val);
void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
unsigned int lane_sel, unsigned int enable);
void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
unsigned int enable);
unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
unsigned int cnt_val);
void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
unsigned int timeout);
void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
unsigned int timeout);
void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int lp);
void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
unsigned int lp);
void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
unsigned int enable);
void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
unsigned int swap_en);
void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
unsigned int hs_zero);
void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim,
unsigned int prep);
void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_clear_all_interrupt(struct mipi_dsim_device *dsim);
unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim);
unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim);
unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device
*dsim);
void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim,
unsigned int di, const unsigned char data0, const unsigned char data1);
void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
unsigned int tx_data);
#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* PWM BACKLIGHT driver for Board based on EXYNOS.
*
* Author: Donghwa Lee <dh09.lee@samsung.com>
*
* Derived from linux/drivers/video/backlight/pwm_backlight.c
*/
#include <common.h>
#include <pwm.h>
#include <linux/types.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
#include <asm/arch/pwm.h>
#include <asm/arch/pwm_backlight.h>
static struct pwm_backlight_data *pwm;
static int exynos_pwm_backlight_update_status(void)
{
int brightness = pwm->brightness;
int max = pwm->max_brightness;
if (brightness == 0) {
pwm_config(pwm->pwm_id, 0, pwm->period);
pwm_disable(pwm->pwm_id);
} else {
pwm_config(pwm->pwm_id,
brightness * pwm->period / max, pwm->period);
pwm_enable(pwm->pwm_id);
}
return 0;
}
int exynos_pwm_backlight_init(struct pwm_backlight_data *pd)
{
pwm = pd;
exynos_pwm_backlight_update_status();
return 0;
}