fbdev changes for 4.9
Main changes:
- amba-cldc: DT backlight support, Nomadik support, Versatile improvements, fixes
- efifb: fix fbcon RGB565 palette
- exynos: remove unused DSI driver
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQIcBAABAgAGBQJX/f2zAAoJEPo9qoy8lh71a4sP/2lEHhzzqBri63FOrLaIGxUh
lBZLSC9mMMzlsJtZhalaKBPe7dQ4EwPPqb7QiIuY71Q7Ap+j6oqaY7m4hgxsH4Zm
jSgWNwVy+LhQ/r4eI355opvvHpGLZaIl6ZN8tFmvcm9bTE4iIkNkxq5Bhk6Bjwkc
8jV8vwYRDfJA4NZgSuGIzRzLkscryy/QApaH536jDx4xoc+un6a759B6FQLAFVkS
GwcjHbr3SSf5aj+0eEaJ59I3rgdPJy8YkFO7PpwKhgqTuCskOXKBvVtmlgfOh2vI
76W2l3SELXCY7VmFkN0sp6xbPWYKIygdT849oF2grAPh/7ykOb0IsT4i4iVSzL29
Nhl/G2s93NGFb3HtO0gAlBq9M0FSLQEywXPbN8GmiGVI5yHVYlFwKOL0H0uQANNl
pCVCHuBkEkY2mX1QoCRhRl1ps9d+VfZzNo8I1JR6A8M52WtYjGiomISYz0dUmSFR
3X5V6TLYFya+1sf3Gkvpl5yLkFB8NkCkdn6kcT1cpU2SyxzmQX5J/EwMlOXL1qSE
d+pZZlMQ2Kli6LeMoAyzfdYgDWldP1CqU06Zcu3aFrAKmIZmLZGgJPq72TApK4L7
o5R795aS6a/0CbihEuJt1LhR3QIQfYXlXJq860YT5he6yBO1wGC+JN2qMEFdU+SH
phnxFjO2n/kI0JjQsewF
=FN42
-----END PGP SIGNATURE-----
Merge tag 'fbdev-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux
Pull fbdev updates from Tomi Valkeinen:
"Main changes:
- amba-cldc: DT backlight support, Nomadik support, Versatile
improvements, fixes
- efifb: fix fbcon RGB565 palette
- exynos: remove unused DSI driver"
* tag 'fbdev-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux: (42 commits)
video: smscufx: remove unused variable
matroxfb: fix size of memcpy
fbdev: ssd1307fb: fix a possible NULL dereference
fbdev: ssd1307fb: constify the device_info pointer
simplefb: Disable and release clocks and regulators in destroy callback
video: fbdev: constify fb_fix_screeninfo and fb_var_screeninfo structures
matroxfb: constify local structures
video: fbdev: i810: add in missing white space in error message text
video: fbdev: add missing \n at end of printk error message
ARM: exynos_defconfig: Remove old non-working MIPI driver
video: fbdev: exynos: Remove old non-working MIPI driver
omapfb: fix return value check in dsi_bind()
MAINTAINERS: update fbdev entries
video: fbdev: offb: Call pci_enable_device() before using the PCI VGA device
fbdev: vfb: simplify memory management
fbdev: vfb: add option for video mode
fbdev: vfb: add description to module parameters
video: fbdev: intelfb: remove impossible condition
fb: adv7393: off by one in probe function
video: fbdev: pxafb: add missing of_node_put() in of_get_pxafb_mode_info()
...
This commit is contained in:
commit
084165a3f8
59 changed files with 1042 additions and 3916 deletions
12
MAINTAINERS
12
MAINTAINERS
|
|
@ -4775,15 +4775,6 @@ L: iommu@lists.linux-foundation.org
|
|||
S: Maintained
|
||||
F: drivers/iommu/exynos-iommu.c
|
||||
|
||||
EXYNOS MIPI DISPLAY DRIVERS
|
||||
M: Inki Dae <inki.dae@samsung.com>
|
||||
M: Donghwa Lee <dh09.lee@samsung.com>
|
||||
M: Kyungmin Park <kyungmin.park@samsung.com>
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/video/fbdev/exynos/exynos_mipi*
|
||||
F: include/video/exynos_mipi*
|
||||
|
||||
EZchip NPS platform support
|
||||
M: Noam Camus <noamc@ezchip.com>
|
||||
S: Supported
|
||||
|
|
@ -4962,12 +4953,9 @@ F: drivers/net/wan/dlci.c
|
|||
F: drivers/net/wan/sdla.c
|
||||
|
||||
FRAMEBUFFER LAYER
|
||||
M: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
|
||||
M: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
W: http://linux-fbdev.sourceforge.net/
|
||||
Q: http://patchwork.kernel.org/project/linux-fbdev/list/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/plagnioj/linux-fbdev.git
|
||||
S: Maintained
|
||||
F: Documentation/fb/
|
||||
F: drivers/video/
|
||||
|
|
|
|||
|
|
@ -168,8 +168,6 @@ CONFIG_DRM_PANEL_SAMSUNG_LD9040=y
|
|||
CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=y
|
||||
CONFIG_DRM_NXP_PTN3460=y
|
||||
CONFIG_DRM_PARADE_PS8622=y
|
||||
CONFIG_EXYNOS_VIDEO=y
|
||||
CONFIG_EXYNOS_MIPI_DSI=y
|
||||
CONFIG_LCD_CLASS_DEVICE=y
|
||||
CONFIG_LCD_PLATFORM=y
|
||||
CONFIG_BACKLIGHT_PWM=y
|
||||
|
|
|
|||
|
|
@ -284,12 +284,14 @@ config FB_PM2_FIFO_DISCONNECT
|
|||
config FB_ARMCLCD
|
||||
tristate "ARM PrimeCell PL110 support"
|
||||
depends on ARM || ARM64 || COMPILE_TEST
|
||||
depends on FB && ARM_AMBA
|
||||
depends on FB && ARM_AMBA && HAS_IOMEM
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
select FB_MODE_HELPERS if OF
|
||||
select VIDEOMODE_HELPERS if OF
|
||||
select BACKLIGHT_LCD_SUPPORT if OF
|
||||
select BACKLIGHT_CLASS_DEVICE if OF
|
||||
help
|
||||
This framebuffer device driver is for the ARM PrimeCell PL110
|
||||
Colour LCD controller. ARM PrimeCells provide the building
|
||||
|
|
@ -305,6 +307,8 @@ config PLAT_VERSATILE_CLCD
|
|||
def_bool ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || ARCH_INTEGRATOR
|
||||
depends on ARM
|
||||
depends on FB_ARMCLCD && FB=y
|
||||
select REGMAP
|
||||
select MFD_SYSCON
|
||||
|
||||
config FB_ACORN
|
||||
bool "Acorn VIDC support"
|
||||
|
|
@ -2443,7 +2447,6 @@ config FB_SIMPLE
|
|||
|
||||
source "drivers/video/fbdev/omap/Kconfig"
|
||||
source "drivers/video/fbdev/omap2/Kconfig"
|
||||
source "drivers/video/fbdev/exynos/Kconfig"
|
||||
source "drivers/video/fbdev/mmp/Kconfig"
|
||||
|
||||
config FB_SH_MOBILE_MERAM
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
obj-y += core/
|
||||
|
||||
obj-$(CONFIG_EXYNOS_VIDEO) += exynos/
|
||||
|
||||
obj-$(CONFIG_FB_MACMODES) += macmodes.o
|
||||
obj-$(CONFIG_FB_WMT_GE_ROPS) += wmt_ge_rops.o
|
||||
|
||||
|
|
@ -79,6 +77,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o
|
|||
obj-$(CONFIG_FB_PVR2) += pvr2fb.o
|
||||
obj-$(CONFIG_FB_VOODOO1) += sstfb.o
|
||||
obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o
|
||||
obj-$(CONFIG_ARCH_NOMADIK) += amba-clcd-nomadik.o
|
||||
obj-$(CONFIG_PLAT_VERSATILE_CLCD) += amba-clcd-versatile.o
|
||||
obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o
|
||||
obj-$(CONFIG_FB_68328) += 68328fb.o
|
||||
|
|
|
|||
259
drivers/video/fbdev/amba-clcd-nomadik.c
Normal file
259
drivers/video/fbdev/amba-clcd-nomadik.c
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/clcd.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "amba-clcd-nomadik.h"
|
||||
|
||||
static struct gpio_desc *grestb;
|
||||
static struct gpio_desc *scen;
|
||||
static struct gpio_desc *scl;
|
||||
static struct gpio_desc *sda;
|
||||
|
||||
static u8 tpg110_readwrite_reg(bool write, u8 address, u8 outval)
|
||||
{
|
||||
int i;
|
||||
u8 inval = 0;
|
||||
|
||||
/* Assert SCEN */
|
||||
gpiod_set_value_cansleep(scen, 1);
|
||||
ndelay(150);
|
||||
/* Hammer out the address */
|
||||
for (i = 5; i >= 0; i--) {
|
||||
if (address & BIT(i))
|
||||
gpiod_set_value_cansleep(sda, 1);
|
||||
else
|
||||
gpiod_set_value_cansleep(sda, 0);
|
||||
ndelay(150);
|
||||
/* Send an SCL pulse */
|
||||
gpiod_set_value_cansleep(scl, 1);
|
||||
ndelay(160);
|
||||
gpiod_set_value_cansleep(scl, 0);
|
||||
ndelay(160);
|
||||
}
|
||||
|
||||
if (write) {
|
||||
/* WRITE */
|
||||
gpiod_set_value_cansleep(sda, 0);
|
||||
} else {
|
||||
/* READ */
|
||||
gpiod_set_value_cansleep(sda, 1);
|
||||
}
|
||||
ndelay(150);
|
||||
/* Send an SCL pulse */
|
||||
gpiod_set_value_cansleep(scl, 1);
|
||||
ndelay(160);
|
||||
gpiod_set_value_cansleep(scl, 0);
|
||||
ndelay(160);
|
||||
|
||||
if (!write)
|
||||
/* HiZ turn-around cycle */
|
||||
gpiod_direction_input(sda);
|
||||
ndelay(150);
|
||||
/* Send an SCL pulse */
|
||||
gpiod_set_value_cansleep(scl, 1);
|
||||
ndelay(160);
|
||||
gpiod_set_value_cansleep(scl, 0);
|
||||
ndelay(160);
|
||||
|
||||
/* Hammer in/out the data */
|
||||
for (i = 7; i >= 0; i--) {
|
||||
int value;
|
||||
|
||||
if (write) {
|
||||
value = !!(outval & BIT(i));
|
||||
gpiod_set_value_cansleep(sda, value);
|
||||
} else {
|
||||
value = gpiod_get_value(sda);
|
||||
if (value)
|
||||
inval |= BIT(i);
|
||||
}
|
||||
ndelay(150);
|
||||
/* Send an SCL pulse */
|
||||
gpiod_set_value_cansleep(scl, 1);
|
||||
ndelay(160);
|
||||
gpiod_set_value_cansleep(scl, 0);
|
||||
ndelay(160);
|
||||
}
|
||||
|
||||
gpiod_direction_output(sda, 0);
|
||||
/* Deassert SCEN */
|
||||
gpiod_set_value_cansleep(scen, 0);
|
||||
/* Satisfies SCEN pulse width */
|
||||
udelay(1);
|
||||
|
||||
return inval;
|
||||
}
|
||||
|
||||
static u8 tpg110_read_reg(u8 address)
|
||||
{
|
||||
return tpg110_readwrite_reg(false, address, 0);
|
||||
}
|
||||
|
||||
static void tpg110_write_reg(u8 address, u8 outval)
|
||||
{
|
||||
tpg110_readwrite_reg(true, address, outval);
|
||||
}
|
||||
|
||||
static void tpg110_startup(struct device *dev)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
dev_info(dev, "TPG110 display enable\n");
|
||||
/* De-assert the reset signal */
|
||||
gpiod_set_value_cansleep(grestb, 0);
|
||||
mdelay(1);
|
||||
dev_info(dev, "de-asserted GRESTB\n");
|
||||
|
||||
/* Test display communication */
|
||||
tpg110_write_reg(0x00, 0x55);
|
||||
val = tpg110_read_reg(0x00);
|
||||
if (val == 0x55)
|
||||
dev_info(dev, "passed communication test\n");
|
||||
val = tpg110_read_reg(0x01);
|
||||
dev_info(dev, "TPG110 chip ID: %d version: %d\n",
|
||||
val>>4, val&0x0f);
|
||||
|
||||
/* Show display resolution */
|
||||
val = tpg110_read_reg(0x02);
|
||||
val &= 7;
|
||||
switch (val) {
|
||||
case 0x0:
|
||||
dev_info(dev, "IN 400x240 RGB -> OUT 800x480 RGB (dual scan)");
|
||||
break;
|
||||
case 0x1:
|
||||
dev_info(dev, "IN 480x272 RGB -> OUT 800x480 RGB (dual scan)");
|
||||
break;
|
||||
case 0x4:
|
||||
dev_info(dev, "480x640 RGB");
|
||||
break;
|
||||
case 0x5:
|
||||
dev_info(dev, "480x272 RGB");
|
||||
break;
|
||||
case 0x6:
|
||||
dev_info(dev, "640x480 RGB");
|
||||
break;
|
||||
case 0x7:
|
||||
dev_info(dev, "800x480 RGB");
|
||||
break;
|
||||
default:
|
||||
dev_info(dev, "ILLEGAL RESOLUTION");
|
||||
break;
|
||||
}
|
||||
|
||||
val = tpg110_read_reg(0x03);
|
||||
dev_info(dev, "resolution is controlled by %s\n",
|
||||
(val & BIT(7)) ? "software" : "hardware");
|
||||
}
|
||||
|
||||
static void tpg110_enable(struct clcd_fb *fb)
|
||||
{
|
||||
struct device *dev = &fb->dev->dev;
|
||||
static bool startup;
|
||||
u8 val;
|
||||
|
||||
if (!startup) {
|
||||
tpg110_startup(dev);
|
||||
startup = true;
|
||||
}
|
||||
|
||||
/* Take chip out of standby */
|
||||
val = tpg110_read_reg(0x03);
|
||||
val |= BIT(0);
|
||||
tpg110_write_reg(0x03, val);
|
||||
}
|
||||
|
||||
static void tpg110_disable(struct clcd_fb *fb)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
dev_info(&fb->dev->dev, "TPG110 display disable\n");
|
||||
val = tpg110_read_reg(0x03);
|
||||
/* Put into standby */
|
||||
val &= ~BIT(0);
|
||||
tpg110_write_reg(0x03, val);
|
||||
}
|
||||
|
||||
static void tpg110_init(struct device *dev, struct device_node *np,
|
||||
struct clcd_board *board)
|
||||
{
|
||||
dev_info(dev, "TPG110 display init\n");
|
||||
|
||||
grestb = devm_get_gpiod_from_child(dev, "grestb", &np->fwnode);
|
||||
if (IS_ERR(grestb)) {
|
||||
dev_err(dev, "no GRESTB GPIO\n");
|
||||
return;
|
||||
}
|
||||
/* This asserts the GRESTB signal, putting the display into reset */
|
||||
gpiod_direction_output(grestb, 1);
|
||||
|
||||
scen = devm_get_gpiod_from_child(dev, "scen", &np->fwnode);
|
||||
if (IS_ERR(scen)) {
|
||||
dev_err(dev, "no SCEN GPIO\n");
|
||||
return;
|
||||
}
|
||||
gpiod_direction_output(scen, 0);
|
||||
scl = devm_get_gpiod_from_child(dev, "scl", &np->fwnode);
|
||||
if (IS_ERR(scl)) {
|
||||
dev_err(dev, "no SCL GPIO\n");
|
||||
return;
|
||||
}
|
||||
gpiod_direction_output(scl, 0);
|
||||
sda = devm_get_gpiod_from_child(dev, "sda", &np->fwnode);
|
||||
if (IS_ERR(sda)) {
|
||||
dev_err(dev, "no SDA GPIO\n");
|
||||
return;
|
||||
}
|
||||
gpiod_direction_output(sda, 0);
|
||||
board->enable = tpg110_enable;
|
||||
board->disable = tpg110_disable;
|
||||
}
|
||||
|
||||
int nomadik_clcd_init_panel(struct clcd_fb *fb,
|
||||
struct device_node *endpoint)
|
||||
{
|
||||
struct device_node *panel;
|
||||
|
||||
panel = of_graph_get_remote_port_parent(endpoint);
|
||||
if (!panel)
|
||||
return -ENODEV;
|
||||
|
||||
if (of_device_is_compatible(panel, "tpo,tpg110"))
|
||||
tpg110_init(&fb->dev->dev, panel, fb->board);
|
||||
else
|
||||
dev_info(&fb->dev->dev, "unknown panel\n");
|
||||
|
||||
/* Unknown panel, fall through */
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nomadik_clcd_init_panel);
|
||||
|
||||
#define PMU_CTRL_OFFSET 0x0000
|
||||
#define PMU_CTRL_LCDNDIF BIT(26)
|
||||
|
||||
int nomadik_clcd_init_board(struct amba_device *adev,
|
||||
struct clcd_board *board)
|
||||
{
|
||||
struct regmap *pmu_regmap;
|
||||
|
||||
dev_info(&adev->dev, "Nomadik CLCD board init\n");
|
||||
pmu_regmap =
|
||||
syscon_regmap_lookup_by_compatible("stericsson,nomadik-pmu");
|
||||
if (IS_ERR(pmu_regmap)) {
|
||||
dev_err(&adev->dev, "could not find PMU syscon regmap\n");
|
||||
return PTR_ERR(pmu_regmap);
|
||||
}
|
||||
regmap_update_bits(pmu_regmap,
|
||||
PMU_CTRL_OFFSET,
|
||||
PMU_CTRL_LCDNDIF,
|
||||
0);
|
||||
dev_info(&adev->dev, "set PMU mux to CLCD mode\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nomadik_clcd_init_board);
|
||||
24
drivers/video/fbdev/amba-clcd-nomadik.h
Normal file
24
drivers/video/fbdev/amba-clcd-nomadik.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef _AMBA_CLCD_NOMADIK_H
|
||||
#define _AMBA_CLCD_NOMADIK_H
|
||||
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
#ifdef CONFIG_ARCH_NOMADIK
|
||||
int nomadik_clcd_init_board(struct amba_device *adev,
|
||||
struct clcd_board *board);
|
||||
int nomadik_clcd_init_panel(struct clcd_fb *fb,
|
||||
struct device_node *endpoint);
|
||||
#else
|
||||
static inline int nomadik_clcd_init_board(struct amba_device *adev,
|
||||
struct clcd_board *board)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int nomadik_clcd_init_panel(struct clcd_fb *fb,
|
||||
struct device_node *endpoint)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* inclusion guard */
|
||||
|
|
@ -3,6 +3,12 @@
|
|||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/clcd.h>
|
||||
#include <linux/platform_data/video-clcd-versatile.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "amba-clcd-versatile.h"
|
||||
|
||||
static struct clcd_panel vga = {
|
||||
.mode = {
|
||||
|
|
@ -178,3 +184,392 @@ void versatile_clcd_remove_dma(struct clcd_fb *fb)
|
|||
dma_free_wc(&fb->dev->dev, fb->fb.fix.smem_len, fb->fb.screen_base,
|
||||
fb->fb.fix.smem_start);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static struct regmap *versatile_syscon_map;
|
||||
static struct regmap *versatile_ib2_map;
|
||||
|
||||
/*
|
||||
* We detect the different syscon types from the compatible strings.
|
||||
*/
|
||||
enum versatile_clcd {
|
||||
INTEGRATOR_CLCD_CM,
|
||||
VERSATILE_CLCD,
|
||||
REALVIEW_CLCD_EB,
|
||||
REALVIEW_CLCD_PB1176,
|
||||
REALVIEW_CLCD_PB11MP,
|
||||
REALVIEW_CLCD_PBA8,
|
||||
REALVIEW_CLCD_PBX,
|
||||
};
|
||||
|
||||
static const struct of_device_id versatile_clcd_of_match[] = {
|
||||
{
|
||||
.compatible = "arm,core-module-integrator",
|
||||
.data = (void *)INTEGRATOR_CLCD_CM,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,versatile-sysreg",
|
||||
.data = (void *)VERSATILE_CLCD,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-eb-syscon",
|
||||
.data = (void *)REALVIEW_CLCD_EB,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-pb1176-syscon",
|
||||
.data = (void *)REALVIEW_CLCD_PB1176,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-pb11mp-syscon",
|
||||
.data = (void *)REALVIEW_CLCD_PB11MP,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-pba8-syscon",
|
||||
.data = (void *)REALVIEW_CLCD_PBA8,
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-pbx-syscon",
|
||||
.data = (void *)REALVIEW_CLCD_PBX,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
/*
|
||||
* Core module CLCD control on the Integrator/CP, bits
|
||||
* 8 thru 19 of the CM_CONTROL register controls a bunch
|
||||
* of CLCD settings.
|
||||
*/
|
||||
#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C
|
||||
#define INTEGRATOR_CLCD_LCDBIASEN BIT(8)
|
||||
#define INTEGRATOR_CLCD_LCDBIASUP BIT(9)
|
||||
#define INTEGRATOR_CLCD_LCDBIASDN BIT(10)
|
||||
/* Bits 11,12,13 controls the LCD type */
|
||||
#define INTEGRATOR_CLCD_LCDMUX_MASK (BIT(11)|BIT(12)|BIT(13))
|
||||
#define INTEGRATOR_CLCD_LCDMUX_LCD24 BIT(11)
|
||||
#define INTEGRATOR_CLCD_LCDMUX_VGA565 BIT(12)
|
||||
#define INTEGRATOR_CLCD_LCDMUX_SHARP (BIT(11)|BIT(12))
|
||||
#define INTEGRATOR_CLCD_LCDMUX_VGA555 BIT(13)
|
||||
#define INTEGRATOR_CLCD_LCDMUX_VGA24 (BIT(11)|BIT(12)|BIT(13))
|
||||
#define INTEGRATOR_CLCD_LCD0_EN BIT(14)
|
||||
#define INTEGRATOR_CLCD_LCD1_EN BIT(15)
|
||||
/* R/L flip on Sharp */
|
||||
#define INTEGRATOR_CLCD_LCD_STATIC1 BIT(16)
|
||||
/* U/D flip on Sharp */
|
||||
#define INTEGRATOR_CLCD_LCD_STATIC2 BIT(17)
|
||||
/* No connection on Sharp */
|
||||
#define INTEGRATOR_CLCD_LCD_STATIC BIT(18)
|
||||
/* 0 = 24bit VGA, 1 = 18bit VGA */
|
||||
#define INTEGRATOR_CLCD_LCD_N24BITEN BIT(19)
|
||||
|
||||
#define INTEGRATOR_CLCD_MASK (INTEGRATOR_CLCD_LCDBIASEN | \
|
||||
INTEGRATOR_CLCD_LCDBIASUP | \
|
||||
INTEGRATOR_CLCD_LCDBIASDN | \
|
||||
INTEGRATOR_CLCD_LCDMUX_MASK | \
|
||||
INTEGRATOR_CLCD_LCD0_EN | \
|
||||
INTEGRATOR_CLCD_LCD1_EN | \
|
||||
INTEGRATOR_CLCD_LCD_STATIC1 | \
|
||||
INTEGRATOR_CLCD_LCD_STATIC2 | \
|
||||
INTEGRATOR_CLCD_LCD_STATIC | \
|
||||
INTEGRATOR_CLCD_LCD_N24BITEN)
|
||||
|
||||
static void integrator_clcd_enable(struct clcd_fb *fb)
|
||||
{
|
||||
struct fb_var_screeninfo *var = &fb->fb.var;
|
||||
u32 val;
|
||||
|
||||
dev_info(&fb->dev->dev, "enable Integrator CLCD connectors\n");
|
||||
|
||||
/* FIXME: really needed? */
|
||||
val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 |
|
||||
INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN;
|
||||
if (var->bits_per_pixel <= 8 ||
|
||||
(var->bits_per_pixel == 16 && var->green.length == 5))
|
||||
/* Pseudocolor, RGB555, BGR555 */
|
||||
val |= INTEGRATOR_CLCD_LCDMUX_VGA555;
|
||||
else if (fb->fb.var.bits_per_pixel <= 16)
|
||||
/* truecolor RGB565 */
|
||||
val |= INTEGRATOR_CLCD_LCDMUX_VGA565;
|
||||
else
|
||||
val = 0; /* no idea for this, don't trust the docs */
|
||||
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
INTEGRATOR_HDR_CTRL_OFFSET,
|
||||
INTEGRATOR_CLCD_MASK,
|
||||
val);
|
||||
}
|
||||
|
||||
/*
|
||||
* This configuration register in the Versatile and RealView
|
||||
* family is uniformly present but appears more and more
|
||||
* unutilized starting with the RealView series.
|
||||
*/
|
||||
#define SYS_CLCD 0x50
|
||||
#define SYS_CLCD_MODE_MASK (BIT(0)|BIT(1))
|
||||
#define SYS_CLCD_MODE_888 0
|
||||
#define SYS_CLCD_MODE_5551 BIT(0)
|
||||
#define SYS_CLCD_MODE_565_R_LSB BIT(1)
|
||||
#define SYS_CLCD_MODE_565_B_LSB (BIT(0)|BIT(1))
|
||||
#define SYS_CLCD_CONNECTOR_MASK (BIT(2)|BIT(3)|BIT(4)|BIT(5))
|
||||
#define SYS_CLCD_NLCDIOON BIT(2)
|
||||
#define SYS_CLCD_VDDPOSSWITCH BIT(3)
|
||||
#define SYS_CLCD_PWR3V5SWITCH BIT(4)
|
||||
#define SYS_CLCD_VDDNEGSWITCH BIT(5)
|
||||
#define SYS_CLCD_TSNSS BIT(6) /* touchscreen enable */
|
||||
#define SYS_CLCD_SSPEXP BIT(7) /* SSP expansion enable */
|
||||
|
||||
/* The Versatile can detect the connected panel type */
|
||||
#define SYS_CLCD_CLCDID_MASK (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12))
|
||||
#define SYS_CLCD_ID_SANYO_3_8 (0x00 << 8)
|
||||
#define SYS_CLCD_ID_SHARP_8_4 (0x01 << 8)
|
||||
#define SYS_CLCD_ID_EPSON_2_2 (0x02 << 8)
|
||||
#define SYS_CLCD_ID_SANYO_2_5 (0x07 << 8)
|
||||
#define SYS_CLCD_ID_VGA (0x1f << 8)
|
||||
|
||||
#define SYS_CLCD_TSNDAV BIT(13) /* data ready from TS */
|
||||
|
||||
/* IB2 control register for the Versatile daughterboard */
|
||||
#define IB2_CTRL 0x00
|
||||
#define IB2_CTRL_LCD_SD BIT(1) /* 1 = shut down LCD */
|
||||
#define IB2_CTRL_LCD_BL_ON BIT(0)
|
||||
#define IB2_CTRL_LCD_MASK (BIT(0)|BIT(1))
|
||||
|
||||
static void versatile_clcd_disable(struct clcd_fb *fb)
|
||||
{
|
||||
dev_info(&fb->dev->dev, "disable Versatile CLCD connectors\n");
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
SYS_CLCD,
|
||||
SYS_CLCD_CONNECTOR_MASK,
|
||||
0);
|
||||
|
||||
/* If we're on an IB2 daughterboard, turn off display */
|
||||
if (versatile_ib2_map) {
|
||||
dev_info(&fb->dev->dev, "disable IB2 display\n");
|
||||
regmap_update_bits(versatile_ib2_map,
|
||||
IB2_CTRL,
|
||||
IB2_CTRL_LCD_MASK,
|
||||
IB2_CTRL_LCD_SD);
|
||||
}
|
||||
}
|
||||
|
||||
static void versatile_clcd_enable(struct clcd_fb *fb)
|
||||
{
|
||||
struct fb_var_screeninfo *var = &fb->fb.var;
|
||||
u32 val = 0;
|
||||
|
||||
dev_info(&fb->dev->dev, "enable Versatile CLCD connectors\n");
|
||||
switch (var->green.length) {
|
||||
case 5:
|
||||
val |= SYS_CLCD_MODE_5551;
|
||||
break;
|
||||
case 6:
|
||||
if (var->red.offset == 0)
|
||||
val |= SYS_CLCD_MODE_565_R_LSB;
|
||||
else
|
||||
val |= SYS_CLCD_MODE_565_B_LSB;
|
||||
break;
|
||||
case 8:
|
||||
val |= SYS_CLCD_MODE_888;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set up the MUX */
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
SYS_CLCD,
|
||||
SYS_CLCD_MODE_MASK,
|
||||
val);
|
||||
|
||||
/* Then enable the display */
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
SYS_CLCD,
|
||||
SYS_CLCD_CONNECTOR_MASK,
|
||||
SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
|
||||
|
||||
/* If we're on an IB2 daughterboard, turn on display */
|
||||
if (versatile_ib2_map) {
|
||||
dev_info(&fb->dev->dev, "enable IB2 display\n");
|
||||
regmap_update_bits(versatile_ib2_map,
|
||||
IB2_CTRL,
|
||||
IB2_CTRL_LCD_MASK,
|
||||
IB2_CTRL_LCD_BL_ON);
|
||||
}
|
||||
}
|
||||
|
||||
static void versatile_clcd_decode(struct clcd_fb *fb, struct clcd_regs *regs)
|
||||
{
|
||||
clcdfb_decode(fb, regs);
|
||||
|
||||
/* Always clear BGR for RGB565: we do the routing externally */
|
||||
if (fb->fb.var.green.length == 6)
|
||||
regs->cntl &= ~CNTL_BGR;
|
||||
}
|
||||
|
||||
static void realview_clcd_disable(struct clcd_fb *fb)
|
||||
{
|
||||
dev_info(&fb->dev->dev, "disable RealView CLCD connectors\n");
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
SYS_CLCD,
|
||||
SYS_CLCD_CONNECTOR_MASK,
|
||||
0);
|
||||
}
|
||||
|
||||
static void realview_clcd_enable(struct clcd_fb *fb)
|
||||
{
|
||||
dev_info(&fb->dev->dev, "enable RealView CLCD connectors\n");
|
||||
regmap_update_bits(versatile_syscon_map,
|
||||
SYS_CLCD,
|
||||
SYS_CLCD_CONNECTOR_MASK,
|
||||
SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
|
||||
}
|
||||
|
||||
struct versatile_panel {
|
||||
u32 id;
|
||||
char *compatible;
|
||||
bool ib2;
|
||||
};
|
||||
|
||||
static const struct versatile_panel versatile_panels[] = {
|
||||
{
|
||||
.id = SYS_CLCD_ID_VGA,
|
||||
.compatible = "VGA",
|
||||
},
|
||||
{
|
||||
.id = SYS_CLCD_ID_SANYO_3_8,
|
||||
.compatible = "sanyo,tm38qv67a02a",
|
||||
},
|
||||
{
|
||||
.id = SYS_CLCD_ID_SHARP_8_4,
|
||||
.compatible = "sharp,lq084v1dg21",
|
||||
},
|
||||
{
|
||||
.id = SYS_CLCD_ID_EPSON_2_2,
|
||||
.compatible = "epson,l2f50113t00",
|
||||
},
|
||||
{
|
||||
.id = SYS_CLCD_ID_SANYO_2_5,
|
||||
.compatible = "sanyo,alr252rgt",
|
||||
.ib2 = true,
|
||||
},
|
||||
};
|
||||
|
||||
static void versatile_panel_probe(struct device *dev,
|
||||
struct device_node *endpoint)
|
||||
{
|
||||
struct versatile_panel const *vpanel = NULL;
|
||||
struct device_node *panel = NULL;
|
||||
u32 val;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* The Versatile CLCD has a panel auto-detection mechanism.
|
||||
* We use this and look for the compatible panel in the
|
||||
* device tree.
|
||||
*/
|
||||
ret = regmap_read(versatile_syscon_map, SYS_CLCD, &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot read CLCD syscon register\n");
|
||||
return;
|
||||
}
|
||||
val &= SYS_CLCD_CLCDID_MASK;
|
||||
|
||||
/* First find corresponding panel information */
|
||||
for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) {
|
||||
vpanel = &versatile_panels[i];
|
||||
|
||||
if (val == vpanel->id) {
|
||||
dev_err(dev, "autodetected panel \"%s\"\n",
|
||||
vpanel->compatible);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ARRAY_SIZE(versatile_panels)) {
|
||||
dev_err(dev, "could not auto-detect panel\n");
|
||||
return;
|
||||
}
|
||||
|
||||
panel = of_graph_get_remote_port_parent(endpoint);
|
||||
if (!panel) {
|
||||
dev_err(dev, "could not locate panel in DT\n");
|
||||
return;
|
||||
}
|
||||
if (!of_device_is_compatible(panel, vpanel->compatible))
|
||||
dev_err(dev, "panel in DT is not compatible with the "
|
||||
"auto-detected panel, continuing anyway\n");
|
||||
|
||||
/*
|
||||
* If we have a Sanyo 2.5" port
|
||||
* that we're running on an IB2 and proceed to look for the
|
||||
* IB2 syscon regmap.
|
||||
*/
|
||||
if (!vpanel->ib2)
|
||||
return;
|
||||
|
||||
versatile_ib2_map = syscon_regmap_lookup_by_compatible(
|
||||
"arm,versatile-ib2-syscon");
|
||||
if (IS_ERR(versatile_ib2_map)) {
|
||||
dev_err(dev, "could not locate IB2 control register\n");
|
||||
versatile_ib2_map = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int versatile_clcd_init_panel(struct clcd_fb *fb,
|
||||
struct device_node *endpoint)
|
||||
{
|
||||
const struct of_device_id *clcd_id;
|
||||
enum versatile_clcd versatile_clcd_type;
|
||||
struct device_node *np;
|
||||
struct regmap *map;
|
||||
struct device *dev = &fb->dev->dev;
|
||||
|
||||
np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match,
|
||||
&clcd_id);
|
||||
if (!np) {
|
||||
dev_err(dev, "no Versatile syscon node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
|
||||
|
||||
map = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(map)) {
|
||||
dev_err(dev, "no Versatile syscon regmap\n");
|
||||
return PTR_ERR(map);
|
||||
}
|
||||
|
||||
switch (versatile_clcd_type) {
|
||||
case INTEGRATOR_CLCD_CM:
|
||||
versatile_syscon_map = map;
|
||||
fb->board->enable = integrator_clcd_enable;
|
||||
/* Override the caps, we have only these */
|
||||
fb->board->caps = CLCD_CAP_5551 | CLCD_CAP_RGB565 |
|
||||
CLCD_CAP_888;
|
||||
dev_info(dev, "set up callbacks for Integrator PL110\n");
|
||||
break;
|
||||
case VERSATILE_CLCD:
|
||||
versatile_syscon_map = map;
|
||||
fb->board->enable = versatile_clcd_enable;
|
||||
fb->board->disable = versatile_clcd_disable;
|
||||
fb->board->decode = versatile_clcd_decode;
|
||||
versatile_panel_probe(dev, endpoint);
|
||||
dev_info(dev, "set up callbacks for Versatile\n");
|
||||
break;
|
||||
case REALVIEW_CLCD_EB:
|
||||
case REALVIEW_CLCD_PB1176:
|
||||
case REALVIEW_CLCD_PB11MP:
|
||||
case REALVIEW_CLCD_PBA8:
|
||||
case REALVIEW_CLCD_PBX:
|
||||
versatile_syscon_map = map;
|
||||
fb->board->enable = realview_clcd_enable;
|
||||
fb->board->disable = realview_clcd_disable;
|
||||
dev_info(dev, "set up callbacks for RealView PL111\n");
|
||||
break;
|
||||
default:
|
||||
dev_info(dev, "unknown Versatile system controller\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(versatile_clcd_init_panel);
|
||||
#endif
|
||||
|
|
|
|||
17
drivers/video/fbdev/amba-clcd-versatile.h
Normal file
17
drivers/video/fbdev/amba-clcd-versatile.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Special local versatile callbacks
|
||||
*/
|
||||
#include <linux/of.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/platform_data/video-clcd-versatile.h>
|
||||
|
||||
#if defined(CONFIG_PLAT_VERSATILE_CLCD) && defined(CONFIG_OF)
|
||||
int versatile_clcd_init_panel(struct clcd_fb *fb,
|
||||
struct device_node *endpoint);
|
||||
#else
|
||||
static inline int versatile_clcd_init_panel(struct clcd_fb *fb,
|
||||
struct device_node *endpoint)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -30,10 +30,14 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <video/display_timing.h>
|
||||
#include <video/of_display_timing.h>
|
||||
#include <video/videomode.h>
|
||||
|
||||
#include "amba-clcd-nomadik.h"
|
||||
#include "amba-clcd-versatile.h"
|
||||
|
||||
#define to_clcd(info) container_of(info, struct clcd_fb, fb)
|
||||
|
||||
/* This is limited to 16 characters when displayed by X startup */
|
||||
|
|
@ -71,6 +75,11 @@ static void clcdfb_disable(struct clcd_fb *fb)
|
|||
if (fb->board->disable)
|
||||
fb->board->disable(fb);
|
||||
|
||||
if (fb->panel->backlight) {
|
||||
fb->panel->backlight->props.power = FB_BLANK_POWERDOWN;
|
||||
backlight_update_status(fb->panel->backlight);
|
||||
}
|
||||
|
||||
val = readl(fb->regs + fb->off_cntl);
|
||||
if (val & CNTL_LCDPWR) {
|
||||
val &= ~CNTL_LCDPWR;
|
||||
|
|
@ -116,6 +125,14 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl)
|
|||
cntl |= CNTL_LCDPWR;
|
||||
writel(cntl, fb->regs + fb->off_cntl);
|
||||
|
||||
/*
|
||||
* Turn on backlight
|
||||
*/
|
||||
if (fb->panel->backlight) {
|
||||
fb->panel->backlight->props.power = FB_BLANK_UNBLANK;
|
||||
backlight_update_status(fb->panel->backlight);
|
||||
}
|
||||
|
||||
/*
|
||||
* finally, enable the interface.
|
||||
*/
|
||||
|
|
@ -211,6 +228,15 @@ clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var)
|
|||
var->blue.length = 4;
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
if (fb->vendor->packed_24_bit_pixels) {
|
||||
var->red.length = 8;
|
||||
var->green.length = 8;
|
||||
var->blue.length = 8;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
/* If we can't do 888, reject */
|
||||
caps &= CLCD_CAP_888;
|
||||
|
|
@ -297,6 +323,12 @@ static int clcdfb_set_par(struct fb_info *info)
|
|||
|
||||
clcdfb_disable(fb);
|
||||
|
||||
/* Some variants must be clocked here */
|
||||
if (fb->vendor->clock_timregs && !fb->clk_enabled) {
|
||||
fb->clk_enabled = true;
|
||||
clk_enable(fb->clk);
|
||||
}
|
||||
|
||||
writel(regs.tim0, fb->regs + CLCD_TIM0);
|
||||
writel(regs.tim1, fb->regs + CLCD_TIM1);
|
||||
writel(regs.tim2, fb->regs + CLCD_TIM2);
|
||||
|
|
@ -551,7 +583,7 @@ static int clcdfb_register(struct clcd_fb *fb)
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static int clcdfb_of_get_dpi_panel_mode(struct device_node *node,
|
||||
struct fb_videomode *mode)
|
||||
struct clcd_panel *clcd_panel)
|
||||
{
|
||||
int err;
|
||||
struct display_timing timing;
|
||||
|
|
@ -563,10 +595,31 @@ static int clcdfb_of_get_dpi_panel_mode(struct device_node *node,
|
|||
|
||||
videomode_from_timing(&timing, &video);
|
||||
|
||||
err = fb_videomode_from_videomode(&video, mode);
|
||||
err = fb_videomode_from_videomode(&video, &clcd_panel->mode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Set up some inversion flags */
|
||||
if (timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
|
||||
clcd_panel->tim2 |= TIM2_IPC;
|
||||
else if (!(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE))
|
||||
/*
|
||||
* To preserve backwards compatibility, the IPC (inverted
|
||||
* pixel clock) flag needs to be set on any display that
|
||||
* doesn't explicitly specify that the pixel clock is
|
||||
* active on the negative or positive edge.
|
||||
*/
|
||||
clcd_panel->tim2 |= TIM2_IPC;
|
||||
|
||||
if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW)
|
||||
clcd_panel->tim2 |= TIM2_IHS;
|
||||
|
||||
if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW)
|
||||
clcd_panel->tim2 |= TIM2_IVS;
|
||||
|
||||
if (timing.flags & DISPLAY_FLAGS_DE_LOW)
|
||||
clcd_panel->tim2 |= TIM2_IOE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -576,11 +629,34 @@ static int clcdfb_snprintf_mode(char *buf, int size, struct fb_videomode *mode)
|
|||
mode->refresh);
|
||||
}
|
||||
|
||||
static int clcdfb_of_get_backlight(struct device_node *endpoint,
|
||||
struct clcd_panel *clcd_panel)
|
||||
{
|
||||
struct device_node *panel;
|
||||
struct device_node *backlight;
|
||||
|
||||
panel = of_graph_get_remote_port_parent(endpoint);
|
||||
if (!panel)
|
||||
return -ENODEV;
|
||||
|
||||
/* Look up the optional backlight phandle */
|
||||
backlight = of_parse_phandle(panel, "backlight", 0);
|
||||
if (backlight) {
|
||||
clcd_panel->backlight = of_find_backlight_by_node(backlight);
|
||||
of_node_put(backlight);
|
||||
|
||||
if (!clcd_panel->backlight)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint,
|
||||
struct fb_videomode *mode)
|
||||
struct clcd_panel *clcd_panel)
|
||||
{
|
||||
int err;
|
||||
struct device_node *panel;
|
||||
struct fb_videomode *mode;
|
||||
char *name;
|
||||
int len;
|
||||
|
||||
|
|
@ -590,11 +666,12 @@ static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint,
|
|||
|
||||
/* Only directly connected DPI panels supported for now */
|
||||
if (of_device_is_compatible(panel, "panel-dpi"))
|
||||
err = clcdfb_of_get_dpi_panel_mode(panel, mode);
|
||||
err = clcdfb_of_get_dpi_panel_mode(panel, clcd_panel);
|
||||
else
|
||||
err = -ENOENT;
|
||||
if (err)
|
||||
return err;
|
||||
mode = &clcd_panel->mode;
|
||||
|
||||
len = clcdfb_snprintf_mode(NULL, 0, mode);
|
||||
name = devm_kzalloc(dev, len + 1, GFP_KERNEL);
|
||||
|
|
@ -616,6 +693,7 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
|
|||
} panels[] = {
|
||||
{ 0x110, 1, 7, 13, CLCD_CAP_5551 },
|
||||
{ 0x110, 0, 8, 16, CLCD_CAP_888 },
|
||||
{ 0x110, 16, 8, 0, CLCD_CAP_888 },
|
||||
{ 0x111, 4, 14, 20, CLCD_CAP_444 },
|
||||
{ 0x111, 3, 11, 19, CLCD_CAP_444 | CLCD_CAP_5551 },
|
||||
{ 0x111, 3, 10, 19, CLCD_CAP_444 | CLCD_CAP_5551 |
|
||||
|
|
@ -625,8 +703,8 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
|
|||
};
|
||||
int i;
|
||||
|
||||
/* Bypass pixel clock divider, data output on the falling edge */
|
||||
fb->panel->tim2 = TIM2_BCD | TIM2_IPC;
|
||||
/* Bypass pixel clock divider */
|
||||
fb->panel->tim2 |= TIM2_BCD;
|
||||
|
||||
/* TFT display, vert. comp. interrupt at the start of the back porch */
|
||||
fb->panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);
|
||||
|
|
@ -643,6 +721,49 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
|
|||
fb->panel->caps = panels[i].caps;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we actually physically connected the R lines to B and
|
||||
* vice versa
|
||||
*/
|
||||
if (r0 != 0 && b0 == 0)
|
||||
fb->panel->bgr_connection = true;
|
||||
|
||||
if (fb->panel->caps && fb->vendor->st_bitmux_control) {
|
||||
/*
|
||||
* Set up the special bits for the Nomadik control register
|
||||
* (other platforms tend to do this through an external
|
||||
* register).
|
||||
*/
|
||||
|
||||
/* Offset of the highest used color */
|
||||
int maxoff = max3(r0, g0, b0);
|
||||
/* Most significant bit out, highest used bit */
|
||||
int msb = 0;
|
||||
|
||||
if (fb->panel->caps & CLCD_CAP_888) {
|
||||
msb = maxoff + 8 - 1;
|
||||
} else if (fb->panel->caps & CLCD_CAP_565) {
|
||||
msb = maxoff + 5 - 1;
|
||||
fb->panel->cntl |= CNTL_ST_1XBPP_565;
|
||||
} else if (fb->panel->caps & CLCD_CAP_5551) {
|
||||
msb = maxoff + 5 - 1;
|
||||
fb->panel->cntl |= CNTL_ST_1XBPP_5551;
|
||||
} else if (fb->panel->caps & CLCD_CAP_444) {
|
||||
msb = maxoff + 4 - 1;
|
||||
fb->panel->cntl |= CNTL_ST_1XBPP_444;
|
||||
}
|
||||
|
||||
/* Send out as many bits as we need */
|
||||
if (msb > 17)
|
||||
fb->panel->cntl |= CNTL_ST_CDWID_24;
|
||||
else if (msb > 15)
|
||||
fb->panel->cntl |= CNTL_ST_CDWID_18;
|
||||
else if (msb > 11)
|
||||
fb->panel->cntl |= CNTL_ST_CDWID_16;
|
||||
else
|
||||
fb->panel->cntl |= CNTL_ST_CDWID_12;
|
||||
}
|
||||
|
||||
return fb->panel->caps ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -658,11 +779,24 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
|
|||
if (!fb->panel)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Fetch the panel endpoint.
|
||||
*/
|
||||
endpoint = of_graph_get_next_endpoint(fb->dev->dev.of_node, NULL);
|
||||
if (!endpoint)
|
||||
return -ENODEV;
|
||||
|
||||
err = clcdfb_of_get_mode(&fb->dev->dev, endpoint, &fb->panel->mode);
|
||||
if (fb->vendor->init_panel) {
|
||||
err = fb->vendor->init_panel(fb, endpoint);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clcdfb_of_get_backlight(endpoint, fb->panel);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clcdfb_of_get_mode(&fb->dev->dev, endpoint, fb->panel);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -693,11 +827,11 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
|
|||
|
||||
if (of_property_read_u32_array(endpoint,
|
||||
"arm,pl11x,tft-r0g0b0-pads",
|
||||
tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) == 0)
|
||||
return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0],
|
||||
tft_r0b0g0[1], tft_r0b0g0[2]);
|
||||
tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) != 0)
|
||||
return -ENOENT;
|
||||
|
||||
return -ENOENT;
|
||||
return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0],
|
||||
tft_r0b0g0[1], tft_r0b0g0[2]);
|
||||
}
|
||||
|
||||
static int clcdfb_of_vram_setup(struct clcd_fb *fb)
|
||||
|
|
@ -818,6 +952,7 @@ static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev)
|
|||
static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
|
||||
{
|
||||
struct clcd_board *board = dev_get_platdata(&dev->dev);
|
||||
struct clcd_vendor_data *vendor = id->data;
|
||||
struct clcd_fb *fb;
|
||||
int ret;
|
||||
|
||||
|
|
@ -827,6 +962,12 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
|
|||
if (!board)
|
||||
return -EINVAL;
|
||||
|
||||
if (vendor->init_board) {
|
||||
ret = vendor->init_board(dev, board);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
goto out;
|
||||
|
|
@ -845,17 +986,18 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
|
|||
}
|
||||
|
||||
fb->dev = dev;
|
||||
fb->vendor = vendor;
|
||||
fb->board = board;
|
||||
|
||||
dev_info(&fb->dev->dev, "PL%03x rev%u at 0x%08llx\n",
|
||||
amba_part(dev), amba_rev(dev),
|
||||
dev_info(&fb->dev->dev, "PL%03x designer %02x rev%u at 0x%08llx\n",
|
||||
amba_part(dev), amba_manf(dev), amba_rev(dev),
|
||||
(unsigned long long)dev->res.start);
|
||||
|
||||
ret = fb->board->setup(fb);
|
||||
if (ret)
|
||||
goto free_fb;
|
||||
|
||||
ret = clcdfb_register(fb);
|
||||
ret = clcdfb_register(fb);
|
||||
if (ret == 0) {
|
||||
amba_set_drvdata(dev, fb);
|
||||
goto out;
|
||||
|
|
@ -891,10 +1033,30 @@ static int clcdfb_remove(struct amba_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct clcd_vendor_data vendor_arm = {
|
||||
/* Sets up the versatile board displays */
|
||||
.init_panel = versatile_clcd_init_panel,
|
||||
};
|
||||
|
||||
static struct clcd_vendor_data vendor_nomadik = {
|
||||
.clock_timregs = true,
|
||||
.packed_24_bit_pixels = true,
|
||||
.st_bitmux_control = true,
|
||||
.init_board = nomadik_clcd_init_board,
|
||||
.init_panel = nomadik_clcd_init_panel,
|
||||
};
|
||||
|
||||
static struct amba_id clcdfb_id_table[] = {
|
||||
{
|
||||
.id = 0x00041110,
|
||||
.mask = 0x000ffffe,
|
||||
.data = &vendor_arm,
|
||||
},
|
||||
/* ST Electronics Nomadik variant */
|
||||
{
|
||||
.id = 0x00180110,
|
||||
.mask = 0x00fffffe,
|
||||
.data = &vendor_nomadik,
|
||||
},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ struct arcfb_par {
|
|||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static struct fb_fix_screeninfo arcfb_fix = {
|
||||
static const struct fb_fix_screeninfo arcfb_fix = {
|
||||
.id = "arcfb",
|
||||
.type = FB_TYPE_PACKED_PIXELS,
|
||||
.visual = FB_VISUAL_MONO01,
|
||||
|
|
@ -89,7 +89,7 @@ static struct fb_fix_screeninfo arcfb_fix = {
|
|||
.accel = FB_ACCEL_NONE,
|
||||
};
|
||||
|
||||
static struct fb_var_screeninfo arcfb_var = {
|
||||
static const struct fb_var_screeninfo arcfb_var = {
|
||||
.xres = 128,
|
||||
.yres = 64,
|
||||
.xres_virtual = 128,
|
||||
|
|
|
|||
|
|
@ -474,7 +474,7 @@ static void chips_hw_init(struct fb_info *p)
|
|||
write_fr(chips_init_fr[i].addr, chips_init_fr[i].data);
|
||||
}
|
||||
|
||||
static struct fb_fix_screeninfo asiliantfb_fix = {
|
||||
static const struct fb_fix_screeninfo asiliantfb_fix = {
|
||||
.id = "Asiliant 69000",
|
||||
.type = FB_TYPE_PACKED_PIXELS,
|
||||
.visual = FB_VISUAL_PSEUDOCOLOR,
|
||||
|
|
@ -483,7 +483,7 @@ static struct fb_fix_screeninfo asiliantfb_fix = {
|
|||
.smem_len = 0x200000, /* 2MB */
|
||||
};
|
||||
|
||||
static struct fb_var_screeninfo asiliantfb_var = {
|
||||
static const struct fb_var_screeninfo asiliantfb_var = {
|
||||
.xres = 640,
|
||||
.yres = 480,
|
||||
.xres_virtual = 640,
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@
|
|||
|
||||
#ifndef CONFIG_PPC_PMAC
|
||||
/* default mode */
|
||||
static struct fb_var_screeninfo default_var = {
|
||||
static const struct fb_var_screeninfo default_var = {
|
||||
/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
|
||||
640, 480, 640, 480, 0, 0, 8, 0,
|
||||
{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
|
||||
|
|
@ -104,7 +104,7 @@ static struct fb_var_screeninfo default_var = {
|
|||
#else /* CONFIG_PPC_PMAC */
|
||||
/* default to 1024x768 at 75Hz on PPC - this will work
|
||||
* on the iMac, the usual 640x480 @ 60Hz doesn't. */
|
||||
static struct fb_var_screeninfo default_var = {
|
||||
static const struct fb_var_screeninfo default_var = {
|
||||
/* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */
|
||||
1024, 768, 1024, 768, 0, 0, 8, 0,
|
||||
{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
|
||||
|
|
@ -375,7 +375,7 @@ static const struct aty128_meminfo ddr_sgram = {
|
|||
.name = "64-bit DDR SGRAM",
|
||||
};
|
||||
|
||||
static struct fb_fix_screeninfo aty128fb_fix = {
|
||||
static const struct fb_fix_screeninfo aty128fb_fix = {
|
||||
.id = "ATY Rage128",
|
||||
.type = FB_TYPE_PACKED_PIXELS,
|
||||
.visual = FB_VISUAL_PSEUDOCOLOR,
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ struct pci_mmap_map {
|
|||
unsigned long prot_mask;
|
||||
};
|
||||
|
||||
static struct fb_fix_screeninfo atyfb_fix = {
|
||||
static const struct fb_fix_screeninfo atyfb_fix = {
|
||||
.id = "ATY Mach64",
|
||||
.type = FB_TYPE_PACKED_PIXELS,
|
||||
.visual = FB_VISUAL_PSEUDOCOLOR,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include "../edid.h"
|
||||
|
||||
static struct fb_var_screeninfo radeonfb_default_var = {
|
||||
static const struct fb_var_screeninfo radeonfb_default_var = {
|
||||
.xres = 640,
|
||||
.yres = 480,
|
||||
.xres_virtual = 640,
|
||||
|
|
|
|||
|
|
@ -375,7 +375,6 @@ static int bfin_adv7393_fb_probe(struct i2c_client *client,
|
|||
{
|
||||
int ret = 0;
|
||||
struct proc_dir_entry *entry;
|
||||
int num_modes = ARRAY_SIZE(known_modes);
|
||||
|
||||
struct adv7393fb_device *fbdev = NULL;
|
||||
|
||||
|
|
@ -384,7 +383,7 @@ static int bfin_adv7393_fb_probe(struct i2c_client *client,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mode > num_modes) {
|
||||
if (mode >= ARRAY_SIZE(known_modes)) {
|
||||
dev_err(&client->dev, "mode %d: not supported", mode);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
|
@ -797,7 +796,7 @@ static struct i2c_driver bfin_adv7393_fb_driver = {
|
|||
|
||||
static int __init bfin_adv7393_fb_driver_init(void)
|
||||
{
|
||||
#if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE)
|
||||
#if IS_ENABLED(CONFIG_I2C_BLACKFIN_TWI)
|
||||
request_module("i2c-bfin-twi");
|
||||
#else
|
||||
request_module("i2c-gpio");
|
||||
|
|
|
|||
|
|
@ -50,9 +50,9 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
|
|||
return 1;
|
||||
|
||||
if (regno < 16) {
|
||||
red >>= 8;
|
||||
green >>= 8;
|
||||
blue >>= 8;
|
||||
red >>= 16 - info->var.red.length;
|
||||
green >>= 16 - info->var.green.length;
|
||||
blue >>= 16 - info->var.blue.length;
|
||||
((u32 *)(info->pseudo_palette))[regno] =
|
||||
(red << info->var.red.offset) |
|
||||
(green << info->var.green.offset) |
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
#
|
||||
# Exynos Video configuration
|
||||
#
|
||||
|
||||
menuconfig EXYNOS_VIDEO
|
||||
tristate "Exynos Video driver support"
|
||||
depends on ARCH_S5PV210 || ARCH_EXYNOS
|
||||
help
|
||||
This enables support for EXYNOS Video device.
|
||||
|
||||
if EXYNOS_VIDEO
|
||||
|
||||
#
|
||||
# MIPI DSI driver
|
||||
#
|
||||
|
||||
config EXYNOS_MIPI_DSI
|
||||
tristate "EXYNOS MIPI DSI driver support."
|
||||
select GENERIC_PHY
|
||||
help
|
||||
This enables support for MIPI-DSI device.
|
||||
|
||||
config EXYNOS_LCD_S6E8AX0
|
||||
tristate "S6E8AX0 MIPI AMOLED LCD Driver"
|
||||
depends on EXYNOS_MIPI_DSI && BACKLIGHT_CLASS_DEVICE
|
||||
depends on (LCD_CLASS_DEVICE = y)
|
||||
default n
|
||||
help
|
||||
If you have an S6E8AX0 MIPI AMOLED LCD Panel, say Y to enable its
|
||||
LCD control driver.
|
||||
|
||||
endif # EXYNOS_VIDEO
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#
|
||||
# Makefile for the exynos video drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_EXYNOS_MIPI_DSI) += exynos-mipi-dsi-mod.o
|
||||
|
||||
exynos-mipi-dsi-mod-objs += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
|
||||
exynos_mipi_dsi_lowlevel.o
|
||||
obj-$(CONFIG_EXYNOS_LCD_S6E8AX0) += s6e8ax0.o
|
||||
|
|
@ -1,574 +0,0 @@
|
|||
/* linux/drivers/video/exynos/exynos_mipi_dsi.c
|
||||
*
|
||||
* Samsung SoC MIPI-DSIM driver.
|
||||
*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd
|
||||
*
|
||||
* InKi Dae, <inki.dae@samsung.com>
|
||||
* Donghwa Lee, <dh09.lee@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <video/exynos_mipi_dsim.h>
|
||||
|
||||
#include "exynos_mipi_dsi_common.h"
|
||||
#include "exynos_mipi_dsi_lowlevel.h"
|
||||
|
||||
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 DEFINE_MUTEX(mipi_dsim_lock);
|
||||
|
||||
static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device
|
||||
*pdev)
|
||||
{
|
||||
return pdev->dev.platform_data;
|
||||
}
|
||||
|
||||
static struct regulator_bulk_data supplies[] = {
|
||||
{ .supply = "vdd11", },
|
||||
{ .supply = "vdd18", },
|
||||
};
|
||||
|
||||
static int exynos_mipi_regulator_enable(struct mipi_dsim_device *dsim)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&dsim->lock);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
|
||||
mutex_unlock(&dsim->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_mipi_regulator_disable(struct mipi_dsim_device *dsim)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&dsim->lock);
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
|
||||
mutex_unlock(&dsim->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* update all register settings to MIPI DSI controller. */
|
||||
static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim)
|
||||
{
|
||||
/*
|
||||
* data from Display controller(FIMD) is not transferred in video mode
|
||||
* but in case of command mode, all settings is not updated to
|
||||
* registers.
|
||||
*/
|
||||
exynos_mipi_dsi_stand_by(dsim, 0);
|
||||
|
||||
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);
|
||||
|
||||
exynos_mipi_dsi_init_interrupt(dsim);
|
||||
|
||||
/*
|
||||
* data from Display controller(FIMD) is transferred in video mode
|
||||
* but in case of command mode, all settings are updated to registers.
|
||||
*/
|
||||
exynos_mipi_dsi_stand_by(dsim, 1);
|
||||
}
|
||||
|
||||
static int exynos_mipi_dsi_early_blank_mode(struct mipi_dsim_device *dsim,
|
||||
int power)
|
||||
{
|
||||
struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
|
||||
struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
|
||||
|
||||
switch (power) {
|
||||
case FB_BLANK_POWERDOWN:
|
||||
if (dsim->suspended)
|
||||
return 0;
|
||||
|
||||
if (client_drv && client_drv->suspend)
|
||||
client_drv->suspend(client_dev);
|
||||
|
||||
clk_disable(dsim->clock);
|
||||
|
||||
exynos_mipi_regulator_disable(dsim);
|
||||
|
||||
dsim->suspended = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
|
||||
{
|
||||
struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
|
||||
struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
|
||||
|
||||
switch (power) {
|
||||
case FB_BLANK_UNBLANK:
|
||||
if (!dsim->suspended)
|
||||
return 0;
|
||||
|
||||
/* lcd panel power on. */
|
||||
if (client_drv && client_drv->power_on)
|
||||
client_drv->power_on(client_dev, 1);
|
||||
|
||||
exynos_mipi_regulator_enable(dsim);
|
||||
|
||||
/* enable MIPI-DSI PHY. */
|
||||
phy_power_on(dsim->phy);
|
||||
|
||||
clk_enable(dsim->clock);
|
||||
|
||||
exynos_mipi_update_cfg(dsim);
|
||||
|
||||
/* set lcd panel sequence commands. */
|
||||
if (client_drv && client_drv->set_sequence)
|
||||
client_drv->set_sequence(client_dev);
|
||||
|
||||
dsim->suspended = false;
|
||||
|
||||
break;
|
||||
case FB_BLANK_NORMAL:
|
||||
/* TODO. */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev)
|
||||
{
|
||||
struct mipi_dsim_ddi *dsim_ddi;
|
||||
|
||||
if (!lcd_dev->name) {
|
||||
pr_err("dsim_lcd_device name is NULL.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL);
|
||||
if (!dsim_ddi) {
|
||||
pr_err("failed to allocate dsim_ddi object.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dsim_ddi->dsim_lcd_dev = lcd_dev;
|
||||
|
||||
mutex_lock(&mipi_dsim_lock);
|
||||
list_add_tail(&dsim_ddi->list, &dsim_ddi_list);
|
||||
mutex_unlock(&mipi_dsim_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mipi_dsim_ddi *exynos_mipi_dsi_find_lcd_device(
|
||||
struct mipi_dsim_lcd_driver *lcd_drv)
|
||||
{
|
||||
struct mipi_dsim_ddi *dsim_ddi, *next;
|
||||
struct mipi_dsim_lcd_device *lcd_dev;
|
||||
|
||||
mutex_lock(&mipi_dsim_lock);
|
||||
|
||||
list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
|
||||
if (!dsim_ddi)
|
||||
goto out;
|
||||
|
||||
lcd_dev = dsim_ddi->dsim_lcd_dev;
|
||||
if (!lcd_dev)
|
||||
continue;
|
||||
|
||||
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;
|
||||
mutex_unlock(&mipi_dsim_lock);
|
||||
|
||||
return dsim_ddi;
|
||||
}
|
||||
|
||||
list_del(&dsim_ddi->list);
|
||||
kfree(dsim_ddi);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&mipi_dsim_lock);
|
||||
|
||||
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->name) {
|
||||
pr_err("dsim_lcd_driver name is NULL.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv);
|
||||
if (!dsim_ddi) {
|
||||
pr_err("mipi_dsim_ddi object not found.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
dsim_ddi->dsim_lcd_drv = lcd_drv;
|
||||
|
||||
pr_info("registered panel driver(%s) to mipi-dsi driver.\n",
|
||||
lcd_drv->name);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exynos_mipi_dsi_register_lcd_driver);
|
||||
|
||||
static struct mipi_dsim_ddi *exynos_mipi_dsi_bind_lcd_ddi(
|
||||
struct mipi_dsim_device *dsim,
|
||||
const char *name)
|
||||
{
|
||||
struct mipi_dsim_ddi *dsim_ddi, *next;
|
||||
struct mipi_dsim_lcd_driver *lcd_drv;
|
||||
struct mipi_dsim_lcd_device *lcd_dev;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&dsim->lock);
|
||||
|
||||
list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
|
||||
lcd_drv = dsim_ddi->dsim_lcd_drv;
|
||||
lcd_dev = dsim_ddi->dsim_lcd_dev;
|
||||
if (!lcd_drv || !lcd_dev ||
|
||||
(dsim->id != dsim_ddi->bus_id))
|
||||
continue;
|
||||
|
||||
dev_dbg(dsim->dev, "lcd_drv->id = %d, lcd_dev->id = %d\n",
|
||||
lcd_drv->id, lcd_dev->id);
|
||||
dev_dbg(dsim->dev, "lcd_dev->bus_id = %d, dsim->id = %d\n",
|
||||
lcd_dev->bus_id, dsim->id);
|
||||
|
||||
if ((strcmp(lcd_drv->name, name) == 0)) {
|
||||
lcd_dev->master = dsim;
|
||||
|
||||
lcd_dev->dev.parent = dsim->dev;
|
||||
dev_set_name(&lcd_dev->dev, "%s", lcd_drv->name);
|
||||
|
||||
ret = device_register(&lcd_dev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dsim->dev,
|
||||
"can't register %s, status %d\n",
|
||||
dev_name(&lcd_dev->dev), ret);
|
||||
mutex_unlock(&dsim->lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dsim->dsim_lcd_dev = lcd_dev;
|
||||
dsim->dsim_lcd_drv = lcd_drv;
|
||||
|
||||
mutex_unlock(&dsim->lock);
|
||||
|
||||
return dsim_ddi;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dsim->lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* define MIPI-DSI Master operations. */
|
||||
static struct mipi_dsim_master_ops master_ops = {
|
||||
.cmd_read = exynos_mipi_dsi_rd_data,
|
||||
.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,
|
||||
.set_early_blank_mode = exynos_mipi_dsi_early_blank_mode,
|
||||
.set_blank_mode = exynos_mipi_dsi_blank_mode,
|
||||
};
|
||||
|
||||
static int exynos_mipi_dsi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct mipi_dsim_device *dsim;
|
||||
struct mipi_dsim_config *dsim_config;
|
||||
struct mipi_dsim_platform_data *dsim_pd;
|
||||
struct mipi_dsim_ddi *dsim_ddi;
|
||||
int ret = -EINVAL;
|
||||
|
||||
dsim = devm_kzalloc(&pdev->dev, sizeof(struct mipi_dsim_device),
|
||||
GFP_KERNEL);
|
||||
if (!dsim) {
|
||||
dev_err(&pdev->dev, "failed to allocate dsim object.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dsim->pd = to_dsim_plat(pdev);
|
||||
dsim->dev = &pdev->dev;
|
||||
dsim->id = pdev->id;
|
||||
|
||||
/* get mipi_dsim_platform_data. */
|
||||
dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd;
|
||||
if (dsim_pd == NULL) {
|
||||
dev_err(&pdev->dev, "failed to get platform data for dsim.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* get mipi_dsim_config. */
|
||||
dsim_config = dsim_pd->dsim_config;
|
||||
if (dsim_config == NULL) {
|
||||
dev_err(&pdev->dev, "failed to get dsim config data.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dsim->dsim_config = dsim_config;
|
||||
dsim->master_ops = &master_ops;
|
||||
|
||||
mutex_init(&dsim->lock);
|
||||
|
||||
ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies),
|
||||
supplies);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dsim->phy = devm_phy_get(&pdev->dev, "dsim");
|
||||
if (IS_ERR(dsim->phy))
|
||||
return PTR_ERR(dsim->phy);
|
||||
|
||||
dsim->clock = devm_clk_get(&pdev->dev, "dsim0");
|
||||
if (IS_ERR(dsim->clock)) {
|
||||
dev_err(&pdev->dev, "failed to get dsim clock source\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
clk_enable(dsim->clock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
dsim->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(dsim->reg_base)) {
|
||||
ret = PTR_ERR(dsim->reg_base);
|
||||
goto error;
|
||||
}
|
||||
|
||||
mutex_init(&dsim->lock);
|
||||
|
||||
/* bind lcd ddi matched with panel name. */
|
||||
dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name);
|
||||
if (!dsim_ddi) {
|
||||
dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n");
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request dsim irq resource\n");
|
||||
goto error;
|
||||
}
|
||||
dsim->irq = ret;
|
||||
|
||||
init_completion(&dsim_wr_comp);
|
||||
init_completion(&dsim_rd_comp);
|
||||
platform_set_drvdata(pdev, dsim);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, dsim->irq,
|
||||
exynos_mipi_dsi_interrupt_handler,
|
||||
IRQF_SHARED, dev_name(&pdev->dev), dsim);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "failed to request dsim irq\n");
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* enable interrupts */
|
||||
exynos_mipi_dsi_init_interrupt(dsim);
|
||||
|
||||
/* initialize mipi-dsi client(lcd panel). */
|
||||
if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe)
|
||||
dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev);
|
||||
|
||||
/* in case mipi-dsi has been enabled by bootloader */
|
||||
if (dsim_pd->enabled) {
|
||||
exynos_mipi_regulator_enable(dsim);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* lcd panel power on. */
|
||||
if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on)
|
||||
dsim_ddi->dsim_lcd_drv->power_on(dsim_ddi->dsim_lcd_dev, 1);
|
||||
|
||||
exynos_mipi_regulator_enable(dsim);
|
||||
|
||||
/* enable MIPI-DSI PHY. */
|
||||
phy_power_on(dsim->phy);
|
||||
|
||||
exynos_mipi_update_cfg(dsim);
|
||||
|
||||
/* set lcd panel sequence commands. */
|
||||
if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->set_sequence)
|
||||
dsim_ddi->dsim_lcd_drv->set_sequence(dsim_ddi->dsim_lcd_dev);
|
||||
|
||||
dsim->suspended = false;
|
||||
|
||||
done:
|
||||
platform_set_drvdata(pdev, dsim);
|
||||
|
||||
dev_dbg(&pdev->dev, "%s() completed successfully (%s mode)\n", __func__,
|
||||
dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB");
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
clk_disable(dsim->clock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_mipi_dsi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
|
||||
struct mipi_dsim_ddi *dsim_ddi, *next;
|
||||
struct mipi_dsim_lcd_driver *dsim_lcd_drv;
|
||||
|
||||
clk_disable(dsim->clock);
|
||||
|
||||
list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
|
||||
if (dsim_ddi) {
|
||||
if (dsim->id != dsim_ddi->bus_id)
|
||||
continue;
|
||||
|
||||
dsim_lcd_drv = dsim_ddi->dsim_lcd_drv;
|
||||
|
||||
if (dsim_lcd_drv->remove)
|
||||
dsim_lcd_drv->remove(dsim_ddi->dsim_lcd_dev);
|
||||
|
||||
kfree(dsim_ddi);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int exynos_mipi_dsi_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
|
||||
struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
|
||||
struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
|
||||
|
||||
disable_irq(dsim->irq);
|
||||
|
||||
if (dsim->suspended)
|
||||
return 0;
|
||||
|
||||
if (client_drv && client_drv->suspend)
|
||||
client_drv->suspend(client_dev);
|
||||
|
||||
/* disable MIPI-DSI PHY. */
|
||||
phy_power_off(dsim->phy);
|
||||
|
||||
clk_disable(dsim->clock);
|
||||
|
||||
exynos_mipi_regulator_disable(dsim);
|
||||
|
||||
dsim->suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_mipi_dsi_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
|
||||
struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
|
||||
struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
|
||||
|
||||
enable_irq(dsim->irq);
|
||||
|
||||
if (!dsim->suspended)
|
||||
return 0;
|
||||
|
||||
/* lcd panel power on. */
|
||||
if (client_drv && client_drv->power_on)
|
||||
client_drv->power_on(client_dev, 1);
|
||||
|
||||
exynos_mipi_regulator_enable(dsim);
|
||||
|
||||
/* enable MIPI-DSI PHY. */
|
||||
phy_power_on(dsim->phy);
|
||||
|
||||
clk_enable(dsim->clock);
|
||||
|
||||
exynos_mipi_update_cfg(dsim);
|
||||
|
||||
/* set lcd panel sequence commands. */
|
||||
if (client_drv && client_drv->set_sequence)
|
||||
client_drv->set_sequence(client_dev);
|
||||
|
||||
dsim->suspended = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops exynos_mipi_dsi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(exynos_mipi_dsi_suspend, exynos_mipi_dsi_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver exynos_mipi_dsi_driver = {
|
||||
.probe = exynos_mipi_dsi_probe,
|
||||
.remove = exynos_mipi_dsi_remove,
|
||||
.driver = {
|
||||
.name = "exynos-mipi-dsim",
|
||||
.pm = &exynos_mipi_dsi_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(exynos_mipi_dsi_driver);
|
||||
|
||||
MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
|
||||
MODULE_DESCRIPTION("Samsung SoC MIPI-DSI driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -1,880 +0,0 @@
|
|||
/* linux/drivers/video/exynos/exynos_mipi_dsi_common.c
|
||||
*
|
||||
* Samsung SoC MIPI-DSI common driver.
|
||||
*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd
|
||||
*
|
||||
* InKi Dae, <inki.dae@samsung.com>
|
||||
* Donghwa Lee, <dh09.lee@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
#include <video/exynos_mipi_dsim.h>
|
||||
|
||||
#include "exynos_mipi_dsi_regs.h"
|
||||
#include "exynos_mipi_dsi_lowlevel.h"
|
||||
#include "exynos_mipi_dsi_common.h"
|
||||
|
||||
#define MIPI_FIFO_TIMEOUT msecs_to_jiffies(250)
|
||||
#define MIPI_RX_FIFO_READ_DONE 0x30800002
|
||||
#define MIPI_MAX_RX_FIFO 20
|
||||
#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)
|
||||
#define TRY_FIFO_CLEAR (10)
|
||||
|
||||
/* 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
|
||||
};
|
||||
|
||||
irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct mipi_dsim_device *dsim = dev_id;
|
||||
unsigned int intsrc, intmsk;
|
||||
|
||||
intsrc = exynos_mipi_dsi_read_interrupt(dsim);
|
||||
intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim);
|
||||
intmsk = ~intmsk & intsrc;
|
||||
|
||||
if (intsrc & INTMSK_RX_DONE) {
|
||||
complete(&dsim_rd_comp);
|
||||
dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n");
|
||||
}
|
||||
if (intsrc & INTMSK_FIFO_EMPTY) {
|
||||
complete(&dsim_wr_comp);
|
||||
dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n");
|
||||
}
|
||||
|
||||
exynos_mipi_dsi_clear_interrupt(dsim, intmsk);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* write long packet to mipi dsi slave
|
||||
* @dsim: mipi dsim device structure.
|
||||
* @data0: packet data to send.
|
||||
* @data1: size of packet data
|
||||
*/
|
||||
static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim,
|
||||
const unsigned char *data0, unsigned int data_size)
|
||||
{
|
||||
unsigned int data_cnt = 0, payload = 0;
|
||||
|
||||
/* in case that data count is more then 4 */
|
||||
for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) {
|
||||
/*
|
||||
* after sending 4bytes per one time,
|
||||
* send remainder data less then 4.
|
||||
*/
|
||||
if ((data_size - data_cnt) < 4) {
|
||||
if ((data_size - data_cnt) == 3) {
|
||||
payload = data0[data_cnt] |
|
||||
data0[data_cnt + 1] << 8 |
|
||||
data0[data_cnt + 2] << 16;
|
||||
dev_dbg(dsim->dev, "count = 3 payload = %x, %x %x %x\n",
|
||||
payload, data0[data_cnt],
|
||||
data0[data_cnt + 1],
|
||||
data0[data_cnt + 2]);
|
||||
} else if ((data_size - data_cnt) == 2) {
|
||||
payload = data0[data_cnt] |
|
||||
data0[data_cnt + 1] << 8;
|
||||
dev_dbg(dsim->dev,
|
||||
"count = 2 payload = %x, %x %x\n", payload,
|
||||
data0[data_cnt],
|
||||
data0[data_cnt + 1]);
|
||||
} else if ((data_size - data_cnt) == 1) {
|
||||
payload = data0[data_cnt];
|
||||
}
|
||||
|
||||
exynos_mipi_dsi_wr_tx_data(dsim, payload);
|
||||
/* send 4bytes per one time. */
|
||||
} else {
|
||||
payload = data0[data_cnt] |
|
||||
data0[data_cnt + 1] << 8 |
|
||||
data0[data_cnt + 2] << 16 |
|
||||
data0[data_cnt + 3] << 24;
|
||||
|
||||
dev_dbg(dsim->dev,
|
||||
"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 data_size)
|
||||
{
|
||||
unsigned int check_rx_ack = 0;
|
||||
|
||||
if (dsim->state == DSIM_STATE_ULPS) {
|
||||
dev_err(dsim->dev, "state is ULPS.\n");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME!!! why does it need this delay? */
|
||||
msleep(20);
|
||||
|
||||
mutex_lock(&dsim->lock);
|
||||
|
||||
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:
|
||||
exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
|
||||
if (check_rx_ack) {
|
||||
/* process response func should be implemented */
|
||||
mutex_unlock(&dsim->lock);
|
||||
return 0;
|
||||
} else {
|
||||
mutex_unlock(&dsim->lock);
|
||||
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. */
|
||||
mutex_unlock(&dsim->lock);
|
||||
return 0;
|
||||
} else {
|
||||
mutex_unlock(&dsim->lock);
|
||||
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:
|
||||
mutex_unlock(&dsim->lock);
|
||||
return 0;
|
||||
|
||||
/* long packet type and null packet */
|
||||
case MIPI_DSI_NULL_PACKET:
|
||||
case MIPI_DSI_BLANKING_PACKET:
|
||||
mutex_unlock(&dsim->lock);
|
||||
return 0;
|
||||
case MIPI_DSI_GENERIC_LONG_WRITE:
|
||||
case MIPI_DSI_DCS_LONG_WRITE:
|
||||
{
|
||||
unsigned int size, payload = 0;
|
||||
reinit_completion(&dsim_wr_comp);
|
||||
|
||||
size = data_size * 4;
|
||||
|
||||
/* if data count is less then 4, then send 3bytes data. */
|
||||
if (data_size < 4) {
|
||||
payload = data0[0] |
|
||||
data0[1] << 8 |
|
||||
data0[2] << 16;
|
||||
|
||||
exynos_mipi_dsi_wr_tx_data(dsim, payload);
|
||||
|
||||
dev_dbg(dsim->dev, "count = %d payload = %x,%x %x %x\n",
|
||||
data_size, payload, data0[0],
|
||||
data0[1], data0[2]);
|
||||
|
||||
/* in case that data count is more then 4 */
|
||||
} else
|
||||
exynos_mipi_dsi_long_data_wr(dsim, data0, data_size);
|
||||
|
||||
/* put data into header fifo */
|
||||
exynos_mipi_dsi_wr_tx_header(dsim, data_id, data_size & 0xff,
|
||||
(data_size & 0xff00) >> 8);
|
||||
|
||||
if (!wait_for_completion_interruptible_timeout(&dsim_wr_comp,
|
||||
MIPI_FIFO_TIMEOUT)) {
|
||||
dev_warn(dsim->dev, "command write timeout.\n");
|
||||
mutex_unlock(&dsim->lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (check_rx_ack) {
|
||||
/* process response func should be implemented. */
|
||||
mutex_unlock(&dsim->lock);
|
||||
return 0;
|
||||
} else {
|
||||
mutex_unlock(&dsim->lock);
|
||||
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. */
|
||||
mutex_unlock(&dsim->lock);
|
||||
return 0;
|
||||
} else {
|
||||
mutex_unlock(&dsim->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
dev_warn(dsim->dev,
|
||||
"data id %x is not supported current DSI spec.\n",
|
||||
data_id);
|
||||
|
||||
mutex_unlock(&dsim->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int exynos_mipi_dsi_long_data_rd(struct mipi_dsim_device *dsim,
|
||||
unsigned int req_size, unsigned int rx_data, u8 *rx_buf)
|
||||
{
|
||||
unsigned int rcv_pkt, i, j;
|
||||
u16 rxsize;
|
||||
|
||||
/* for long packet */
|
||||
rxsize = (u16)((rx_data & 0x00ffff00) >> 8);
|
||||
dev_dbg(dsim->dev, "mipi dsi rx size : %d\n", rxsize);
|
||||
if (rxsize != req_size) {
|
||||
dev_dbg(dsim->dev,
|
||||
"received size mismatch received: %d, requested: %d\n",
|
||||
rxsize, req_size);
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < (rxsize >> 2); i++) {
|
||||
rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
|
||||
dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt);
|
||||
for (j = 0; j < 4; j++) {
|
||||
rx_buf[(i * 4) + j] =
|
||||
(u8)(rcv_pkt >> (j * 8)) & 0xff;
|
||||
dev_dbg(dsim->dev, "received value : %02x\n",
|
||||
(rcv_pkt >> (j * 8)) & 0xff);
|
||||
}
|
||||
}
|
||||
if (rxsize % 4) {
|
||||
rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
|
||||
dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt);
|
||||
for (j = 0; j < (rxsize % 4); j++) {
|
||||
rx_buf[(i * 4) + j] =
|
||||
(u8)(rcv_pkt >> (j * 8)) & 0xff;
|
||||
dev_dbg(dsim->dev, "received value : %02x\n",
|
||||
(rcv_pkt >> (j * 8)) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
return rxsize;
|
||||
|
||||
err:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static unsigned int exynos_mipi_dsi_response_size(unsigned int req_size)
|
||||
{
|
||||
switch (req_size) {
|
||||
case 1:
|
||||
return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE;
|
||||
case 2:
|
||||
return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE;
|
||||
default:
|
||||
return MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE;
|
||||
}
|
||||
}
|
||||
|
||||
int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id,
|
||||
unsigned int data0, unsigned int req_size, u8 *rx_buf)
|
||||
{
|
||||
unsigned int rx_data, rcv_pkt, i;
|
||||
u8 response = 0;
|
||||
u16 rxsize;
|
||||
|
||||
if (dsim->state == DSIM_STATE_ULPS) {
|
||||
dev_err(dsim->dev, "state is ULPS.\n");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME!!! */
|
||||
msleep(20);
|
||||
|
||||
mutex_lock(&dsim->lock);
|
||||
reinit_completion(&dsim_rd_comp);
|
||||
exynos_mipi_dsi_rd_tx_header(dsim,
|
||||
MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, req_size);
|
||||
|
||||
response = exynos_mipi_dsi_response_size(req_size);
|
||||
|
||||
switch (data_id) {
|
||||
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_rd_tx_header(dsim,
|
||||
data_id, data0);
|
||||
/* process response func should be implemented. */
|
||||
break;
|
||||
default:
|
||||
dev_warn(dsim->dev,
|
||||
"data id %x is not supported current DSI spec.\n",
|
||||
data_id);
|
||||
|
||||
mutex_unlock(&dsim->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_interruptible_timeout(&dsim_rd_comp,
|
||||
MIPI_FIFO_TIMEOUT)) {
|
||||
pr_err("RX done interrupt timeout\n");
|
||||
mutex_unlock(&dsim->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
|
||||
rx_data = exynos_mipi_dsi_rd_rx_fifo(dsim);
|
||||
|
||||
if ((u8)(rx_data & 0xff) != response) {
|
||||
printk(KERN_ERR
|
||||
"mipi dsi wrong response rx_data : %x, response:%x\n",
|
||||
rx_data, response);
|
||||
goto clear_rx_fifo;
|
||||
}
|
||||
|
||||
if (req_size <= 2) {
|
||||
/* for short packet */
|
||||
for (i = 0; i < req_size; i++)
|
||||
rx_buf[i] = (rx_data >> (8 + (i * 8))) & 0xff;
|
||||
rxsize = req_size;
|
||||
} else {
|
||||
/* for long packet */
|
||||
rxsize = exynos_mipi_dsi_long_data_rd(dsim, req_size, rx_data,
|
||||
rx_buf);
|
||||
if (rxsize != req_size)
|
||||
goto clear_rx_fifo;
|
||||
}
|
||||
|
||||
rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
|
||||
|
||||
msleep(20);
|
||||
|
||||
if (rcv_pkt != MIPI_RX_FIFO_READ_DONE) {
|
||||
dev_info(dsim->dev,
|
||||
"Can't found RX FIFO READ DONE FLAG : %x\n", rcv_pkt);
|
||||
goto clear_rx_fifo;
|
||||
}
|
||||
|
||||
mutex_unlock(&dsim->lock);
|
||||
|
||||
return rxsize;
|
||||
|
||||
clear_rx_fifo:
|
||||
i = 0;
|
||||
while (1) {
|
||||
rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
|
||||
if ((rcv_pkt == MIPI_RX_FIFO_READ_DONE)
|
||||
|| (i > MIPI_MAX_RX_FIFO))
|
||||
break;
|
||||
dev_dbg(dsim->dev,
|
||||
"mipi dsi clear rx fifo : %08x\n", rcv_pkt);
|
||||
i++;
|
||||
}
|
||||
dev_info(dsim->dev,
|
||||
"mipi dsi rx done count : %d, rcv_pkt : %08x\n", i, rcv_pkt);
|
||||
|
||||
mutex_unlock(&dsim->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static 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_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;
|
||||
}
|
||||
|
||||
static 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) {
|
||||
dev_warn(dsim->dev, "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;
|
||||
dev_dbg(dsim->dev, "dfvco = %lu, dfin_pll = %lu, main_divider = %d\n",
|
||||
dfvco, dfin_pll, main_divider);
|
||||
if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ)
|
||||
dev_warn(dsim->dev, "fvco range should be 500MHz ~ 1000MHz\n");
|
||||
|
||||
dpll_out = dfvco / (1 << scaler);
|
||||
dev_dbg(dsim->dev, "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;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(dsim->dev, "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 */
|
||||
dev_dbg(dsim->dev, "FOUT of mipi dphy pll is %luMHz\n",
|
||||
(dpll_out / MHZ));
|
||||
|
||||
return dpll_out;
|
||||
}
|
||||
|
||||
static 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) {
|
||||
dev_err(dsim->dev,
|
||||
"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) {
|
||||
dev_warn(dsim->dev, "this project is not support\n");
|
||||
dev_warn(dsim->dev,
|
||||
"external clock source for MIPI DSIM.\n");
|
||||
} else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS) {
|
||||
dev_warn(dsim->dev, "this project is not support\n");
|
||||
dev_warn(dsim->dev,
|
||||
"external clock source for MIPI DSIM\n");
|
||||
}
|
||||
|
||||
/* escape clock divider */
|
||||
esc_div = byte_clk / (dsim->dsim_config->esc_clk);
|
||||
dev_dbg(dsim->dev,
|
||||
"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;
|
||||
dev_dbg(dsim->dev,
|
||||
"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);
|
||||
|
||||
dev_dbg(dsim->dev, "byte clock is %luMHz\n",
|
||||
(byte_clk / MHZ));
|
||||
dev_dbg(dsim->dev, "escape clock that user's need is %lu\n",
|
||||
(dsim->dsim_config->esc_clk / MHZ));
|
||||
dev_dbg(dsim->dev, "escape clock divider is %x\n", esc_div);
|
||||
dev_dbg(dsim->dev, "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);
|
||||
dev_warn(dsim->dev, "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;
|
||||
dev_warn(dsim->dev, "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:
|
||||
dev_info(dsim->dev, "data lane is invalid.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
exynos_mipi_dsi_sw_reset(dsim);
|
||||
exynos_mipi_dsi_func_reset(dsim);
|
||||
|
||||
exynos_mipi_dsi_dp_dn_swap(dsim, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim)
|
||||
{
|
||||
unsigned int src = 0;
|
||||
|
||||
src = (INTSRC_SFR_FIFO_EMPTY | INTSRC_RX_DATA_DONE);
|
||||
exynos_mipi_dsi_set_interrupt(dsim, src, 1);
|
||||
|
||||
src = 0;
|
||||
src = ~(INTMSK_RX_DONE | INTMSK_FIFO_EMPTY);
|
||||
exynos_mipi_dsi_set_interrupt_mask(dsim, src, 1);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim,
|
||||
unsigned int enable)
|
||||
{
|
||||
|
||||
/* consider Main display and Sub display. */
|
||||
|
||||
exynos_mipi_dsi_set_main_stand_by(dsim, enable);
|
||||
}
|
||||
|
||||
int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
|
||||
struct mipi_dsim_config *dsim_config)
|
||||
{
|
||||
struct mipi_dsim_platform_data *dsim_pd;
|
||||
struct fb_videomode *timing;
|
||||
|
||||
dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd;
|
||||
timing = (struct fb_videomode *)dsim_pd->lcd_panel_info;
|
||||
|
||||
/* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */
|
||||
if (dsim_config->e_interface == (u32) DSIM_VIDEO) {
|
||||
if (dsim_config->auto_vertical_cnt == 0) {
|
||||
exynos_mipi_dsi_set_main_disp_vporch(dsim,
|
||||
dsim_config->cmd_allow,
|
||||
timing->lower_margin,
|
||||
timing->upper_margin);
|
||||
exynos_mipi_dsi_set_main_disp_hporch(dsim,
|
||||
timing->right_margin,
|
||||
timing->left_margin);
|
||||
exynos_mipi_dsi_set_main_disp_sync_area(dsim,
|
||||
timing->vsync_len,
|
||||
timing->hsync_len);
|
||||
}
|
||||
}
|
||||
|
||||
exynos_mipi_dsi_set_main_disp_resol(dsim, timing->xres,
|
||||
timing->yres);
|
||||
|
||||
exynos_mipi_dsi_display_config(dsim, dsim_config);
|
||||
|
||||
dev_info(dsim->dev, "lcd panel ==> width = %d, height = %d\n",
|
||||
timing->xres, timing->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) {
|
||||
dev_err(dsim->dev,
|
||||
"DSI Master is not stop state.\n");
|
||||
dev_err(dsim->dev,
|
||||
"Check initialization process\n");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (time_out != 0) {
|
||||
dev_info(dsim->dev,
|
||||
"DSI Master driver has been completed.\n");
|
||||
dev_info(dsim->dev, "DSI Master state is stop state\n");
|
||||
}
|
||||
|
||||
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:
|
||||
dev_info(dsim->dev, "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) {
|
||||
dev_warn(dsim->dev, "DSIM is not in stop state.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dsim->e_clk_src == DSIM_EXT_CLK_BYPASS) {
|
||||
dev_warn(dsim->dev, "clock source is external bypass.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
|
||||
unsigned int mode)
|
||||
{
|
||||
if (mode) {
|
||||
if (dsim->state != DSIM_STATE_HSCLKEN) {
|
||||
dev_err(dsim->dev, "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) {
|
||||
dev_err(dsim->dev,
|
||||
"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;
|
||||
}
|
||||
|
||||
int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim,
|
||||
unsigned int val)
|
||||
{
|
||||
int try = TRY_FIFO_CLEAR;
|
||||
|
||||
exynos_mipi_dsi_sw_reset_release(dsim);
|
||||
exynos_mipi_dsi_func_reset(dsim);
|
||||
|
||||
do {
|
||||
if (exynos_mipi_dsi_get_sw_reset_release(dsim)) {
|
||||
exynos_mipi_dsi_init_interrupt(dsim);
|
||||
dev_dbg(dsim->dev, "reset release done.\n");
|
||||
return 0;
|
||||
}
|
||||
} while (--try);
|
||||
|
||||
dev_err(dsim->dev, "failed to clear dsim fifo.\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
|
||||
MODULE_DESCRIPTION("Samsung SoC MIPI-DSI common driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue