fbdev changes for 4.5
* pxafb: device-tree support * An unsafe kernel parameter 'lockless_register_fb' for debugging problems happening while inside the console lock * Small miscellaneous fixes & cleanups * omapdss: add writeback support functions * Separation of omapfb and omapdrm (see below) About the separation of omapfb and omapdrm, see http://permalink.gmane.org/gmane.comp.video.dri.devel/143151 for longer story. The short version: omapfb and omapdrm have shared low level drivers (omapdss and panel drivers), making further development of omapdrm difficult. After these patches omapfb and omapdrm have their own versions of the drivers, which are more or less direct copies for now but will diverge soon. This also means that omapfb (everything under drivers/video/fbdev/omap2/) is now in maintenance mode, and all new development will be done for omapdrm (drivers/gpu/drm/omapdrm/). -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWnKg1AAoJEPo9qoy8lh71lrsP/RwwG8FDMl2tgwcsKVa/VlbF oez/CaNeZ9Jz0qd5RbIzIS9QdbL0AZugg4twwl76UbHWT477Z3EbUmpw++78kasr RFKDWYqMbxw3kshRDyALinGQmxPOPjNnc5mt9CYKzK4x0pJSLBmZc8qaNK3L4a5a eLJ3h6UhQDY61D04qr+LuTCETAxNR78x+NNIG7vYa9oS0ZDDrhlDyVPw4akPDMS6 6Y5NgtRL1h2mq2hLBgTDCrwx3p7yZbnkSRKbpFnw/yddiXilND1d75JoW+0F6vKW U8DiRKxYtHNBdry4HlpRwufT52wkmtA/2puCW5Smw8araQ7R+s+wOt/1HAYQM72g 8UCmNFMbhBpk8x8pT24ja4wyTLM9gaZqG9MWHLPEPbE6WicxSbqEAvIX9sakXLv6 dDaf1SHZ+DFpq0jOwC8Rcnx1JFeeNNDf5cJb2pZI2Zka5jayQRTdbxeZGGnpFzu1 1ZMiNQ24U+n9hgjV9QMiCW24TEBXFhFTf0Nlne3VP7qUbmvLqMUdGxGwM+b25/El SW/peryWglxsn5EBA7XybK+RTYxbjDtD5a8SOjD2YTNqVVVFHgf7z05SfSmYO5yi H67eDqdt0YsEGG87I8hv3eKM7FSRlYAywTC2mPfSOJ3+/G+18OU/voepcJHZ15x7 SO3e/TFTrtglJzjVzX8j =Nrji -----END PGP SIGNATURE----- Merge tag 'fbdev-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux Pull fbdev updates from Tomi Valkeinen: "Summary: - pxafb: device-tree support - An unsafe kernel parameter 'lockless_register_fb' for debugging problems happening while inside the console lock - Small miscellaneous fixes & cleanups - omapdss: add writeback support functions - Separation of omapfb and omapdrm (see below) About the separation of omapfb and omapdrm, see http://permalink.gmane.org/gmane.comp.video.dri.devel/143151 for longer story. The short version: omapfb and omapdrm have shared low level drivers (omapdss and panel drivers), making further development of omapdrm difficult. After these patches omapfb and omapdrm have their own versions of the drivers, which are more or less direct copies for now but will diverge soon. This also means that omapfb (everything under drivers/video/fbdev/omap2/) is now in maintenance mode, and all new development will be done for omapdrm (drivers/gpu/drm/omapdrm/)" * tag 'fbdev-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux: (49 commits) video: fbdev: pxafb: fix out of memory error path drm/omap: make omapdrm select OMAP2_DSS drm/omap: move omapdss & displays under omapdrm omapfb: move vrfb into omapfb omapfb: take omapfb's private omapdss into use omapfb/displays: change CONFIG_DISPLAY_* to CONFIG_FB_OMAP2_* omapfb/dss: change CONFIG_OMAP* to CONFIG_FB_OMAP* omapdss: remove CONFIG_OMAP2_DSS_VENC from omapdss.h omapfb: copy omapdss & displays for omapfb omapfb: allow compilation only if DRM_OMAP is disabled fbdev: omap2: panel-dpi: simplify gpio setting fbdev: omap2: panel-dpi: in .disable first disable backlight then display OMAPDSS: DSS: fix a warning message video: omapdss: delete unneeded of_node_put OMAPDSS: DISPC: Remove boolean comparisons OMAPDSS: DSI: cleanup DSI_IRQ_ERROR_MASK define OMAPDSS: remove extra out == NULL checks OMAPDSS: change internal dispc functions to static OMAPDSS: make a two dss feat funcs internal to omapdss OMAPDSS: remove extra EXPORT_SYMBOLs ...
This commit is contained in:
commit
d43fb9f3c5
|
@ -47,10 +47,14 @@ Required properties:
|
|||
- a8b8g8r8 (32-bit pixels, d[31:24]=a, d[23:16]=b, d[15:8]=g, d[7:0]=r).
|
||||
|
||||
Optional properties:
|
||||
- clocks : List of clocks used by the framebuffer. Clocks listed here
|
||||
are expected to already be configured correctly. The OS must
|
||||
ensure these clocks are not modified or disabled while the
|
||||
simple framebuffer remains active.
|
||||
- clocks : List of clocks used by the framebuffer.
|
||||
- *-supply : Any number of regulators used by the framebuffer. These should
|
||||
be named according to the names in the device's design.
|
||||
|
||||
The above resources are expected to already be configured correctly.
|
||||
The OS must ensure they are not modified or disabled while the simple
|
||||
framebuffer remains active.
|
||||
|
||||
- display : phandle pointing to the primary display hardware node
|
||||
|
||||
Example:
|
||||
|
@ -68,6 +72,7 @@ chosen {
|
|||
stride = <(1600 * 2)>;
|
||||
format = "r5g6b5";
|
||||
clocks = <&ahb_gates 36>, <&ahb_gates 43>, <&ahb_gates 44>;
|
||||
lcd-supply = <®_dc1sw>;
|
||||
display = <&lcdc0>;
|
||||
};
|
||||
stdout-path = "display0";
|
||||
|
|
|
@ -62,7 +62,7 @@ obj-$(CONFIG_DRM_ARMADA) += armada/
|
|||
obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/
|
||||
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
|
||||
obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
|
||||
obj-$(CONFIG_DRM_OMAP) += omapdrm/
|
||||
obj-y += omapdrm/
|
||||
obj-y += tilcdc/
|
||||
obj-$(CONFIG_DRM_QXL) += qxl/
|
||||
obj-$(CONFIG_DRM_BOCHS) += bochs/
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
|
||||
config DRM_OMAP
|
||||
tristate "OMAP DRM"
|
||||
depends on DRM
|
||||
depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM
|
||||
depends on OMAP2_DSS
|
||||
select OMAP2_DSS
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_FB_HELPER
|
||||
select FB_SYS_FILLRECT
|
||||
|
@ -14,13 +13,18 @@ config DRM_OMAP
|
|||
help
|
||||
DRM display driver for OMAP2/3/4 based boards.
|
||||
|
||||
if DRM_OMAP
|
||||
|
||||
config DRM_OMAP_NUM_CRTCS
|
||||
int "Number of CRTCs"
|
||||
range 1 10
|
||||
default 1 if ARCH_OMAP2 || ARCH_OMAP3
|
||||
default 2 if ARCH_OMAP4
|
||||
depends on DRM_OMAP
|
||||
help
|
||||
Select the number of video overlays which can be used as framebuffers.
|
||||
The remaining overlays are reserved for video.
|
||||
|
||||
source "drivers/gpu/drm/omapdrm/dss/Kconfig"
|
||||
source "drivers/gpu/drm/omapdrm/displays/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
# Direct Rendering Infrastructure (DRI)
|
||||
#
|
||||
|
||||
obj-y += dss/
|
||||
obj-y += displays/
|
||||
|
||||
ccflags-y := -Iinclude/drm -Werror
|
||||
omapdrm-y := omap_drv.o \
|
||||
omap_irq.o \
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
menu "OMAP Display Device Drivers (new device model)"
|
||||
depends on OMAP2_DSS
|
||||
menu "OMAPDRM External Display Device Drivers"
|
||||
|
||||
config DISPLAY_ENCODER_OPA362
|
||||
tristate "OPA362 external analog amplifier"
|
|
@ -83,7 +83,6 @@ static int panel_dpi_enable(struct omap_dss_device *dssdev)
|
|||
if (r)
|
||||
return r;
|
||||
|
||||
if (ddata->enable_gpio)
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio))
|
||||
|
@ -102,12 +101,11 @@ static void panel_dpi_disable(struct omap_dss_device *dssdev)
|
|||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (ddata->enable_gpio)
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio))
|
||||
gpio_set_value_cansleep(ddata->backlight_gpio, 0);
|
||||
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
|
@ -476,7 +476,7 @@ static void dispc_mgr_disable_lcd_out(enum omap_channel channel)
|
|||
int r;
|
||||
u32 irq;
|
||||
|
||||
if (dispc_mgr_is_enabled(channel) == false)
|
||||
if (!dispc_mgr_is_enabled(channel))
|
||||
return;
|
||||
|
||||
/*
|
||||
|
@ -524,7 +524,7 @@ static void dispc_mgr_enable_digit_out(void)
|
|||
int r;
|
||||
u32 irq_mask;
|
||||
|
||||
if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) == true)
|
||||
if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
|
||||
return;
|
||||
|
||||
/*
|
||||
|
@ -562,7 +562,7 @@ static void dispc_mgr_disable_digit_out(void)
|
|||
u32 irq_mask;
|
||||
int num_irqs;
|
||||
|
||||
if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) == false)
|
||||
if (!dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
|
||||
return;
|
||||
|
||||
/*
|
|
@ -99,6 +99,11 @@ struct dispc_features {
|
|||
|
||||
/* PIXEL_INC is not added to the last pixel of a line */
|
||||
bool last_pixel_inc_missing:1;
|
||||
|
||||
/* POL_FREQ has ALIGN bit */
|
||||
bool supports_sync_align:1;
|
||||
|
||||
bool has_writeback:1;
|
||||
};
|
||||
|
||||
#define DISPC_MAX_NR_FIFOS 5
|
||||
|
@ -246,6 +251,11 @@ struct color_conv_coef {
|
|||
int full_range;
|
||||
};
|
||||
|
||||
static unsigned long dispc_fclk_rate(void);
|
||||
static unsigned long dispc_core_clk_rate(void);
|
||||
static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
|
||||
static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel);
|
||||
|
||||
static unsigned long dispc_plane_pclk_rate(enum omap_plane plane);
|
||||
static unsigned long dispc_plane_lclk_rate(enum omap_plane plane);
|
||||
|
||||
|
@ -571,7 +581,7 @@ EXPORT_SYMBOL(dispc_mgr_go_busy);
|
|||
|
||||
void dispc_mgr_go(enum omap_channel channel)
|
||||
{
|
||||
WARN_ON(dispc_mgr_is_enabled(channel) == false);
|
||||
WARN_ON(!dispc_mgr_is_enabled(channel));
|
||||
WARN_ON(dispc_mgr_go_busy(channel));
|
||||
|
||||
DSSDBG("GO %s\n", mgr_desc[channel].name);
|
||||
|
@ -707,19 +717,20 @@ static void dispc_setup_color_conv_coef(void)
|
|||
{
|
||||
int i;
|
||||
int num_ovl = dss_feat_get_num_ovls();
|
||||
int num_wb = dss_feat_get_num_wbs();
|
||||
const struct color_conv_coef ctbl_bt601_5_ovl = {
|
||||
/* YUV -> RGB */
|
||||
298, 409, 0, 298, -208, -100, 298, 0, 517, 0,
|
||||
};
|
||||
const struct color_conv_coef ctbl_bt601_5_wb = {
|
||||
66, 112, -38, 129, -94, -74, 25, -18, 112, 0,
|
||||
/* RGB -> YUV */
|
||||
66, 129, 25, 112, -94, -18, -38, -74, 112, 0,
|
||||
};
|
||||
|
||||
for (i = 1; i < num_ovl; i++)
|
||||
dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_ovl);
|
||||
|
||||
for (; i < num_wb; i++)
|
||||
dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_wb);
|
||||
if (dispc.feat->has_writeback)
|
||||
dispc_ovl_write_color_conv_coef(OMAP_DSS_WB, &ctbl_bt601_5_wb);
|
||||
}
|
||||
|
||||
static void dispc_ovl_set_ba0(enum omap_plane plane, u32 paddr)
|
||||
|
@ -970,6 +981,10 @@ void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel)
|
|||
return;
|
||||
}
|
||||
break;
|
||||
case OMAP_DSS_CHANNEL_WB:
|
||||
chan = 0;
|
||||
chan2 = 3;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
return;
|
||||
|
@ -988,7 +1003,6 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane)
|
|||
{
|
||||
int shift;
|
||||
u32 val;
|
||||
enum omap_channel channel;
|
||||
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
|
@ -1006,23 +1020,23 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane)
|
|||
|
||||
val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
|
||||
|
||||
if (dss_has_feature(FEAT_MGR_LCD3)) {
|
||||
if (FLD_GET(val, 31, 30) == 0)
|
||||
channel = FLD_GET(val, shift, shift);
|
||||
else if (FLD_GET(val, 31, 30) == 1)
|
||||
channel = OMAP_DSS_CHANNEL_LCD2;
|
||||
else
|
||||
channel = OMAP_DSS_CHANNEL_LCD3;
|
||||
} else if (dss_has_feature(FEAT_MGR_LCD2)) {
|
||||
if (FLD_GET(val, 31, 30) == 0)
|
||||
channel = FLD_GET(val, shift, shift);
|
||||
else
|
||||
channel = OMAP_DSS_CHANNEL_LCD2;
|
||||
} else {
|
||||
channel = FLD_GET(val, shift, shift);
|
||||
}
|
||||
if (FLD_GET(val, shift, shift) == 1)
|
||||
return OMAP_DSS_CHANNEL_DIGIT;
|
||||
|
||||
return channel;
|
||||
if (!dss_has_feature(FEAT_MGR_LCD2))
|
||||
return OMAP_DSS_CHANNEL_LCD;
|
||||
|
||||
switch (FLD_GET(val, 31, 30)) {
|
||||
case 0:
|
||||
default:
|
||||
return OMAP_DSS_CHANNEL_LCD;
|
||||
case 1:
|
||||
return OMAP_DSS_CHANNEL_LCD2;
|
||||
case 2:
|
||||
return OMAP_DSS_CHANNEL_LCD3;
|
||||
case 3:
|
||||
return OMAP_DSS_CHANNEL_WB;
|
||||
}
|
||||
}
|
||||
|
||||
void dispc_wb_set_channel_in(enum dss_writeback_channel channel)
|
||||
|
@ -1050,6 +1064,8 @@ static void dispc_configure_burst_sizes(void)
|
|||
/* Configure burst size always to maximum size */
|
||||
for (i = 0; i < dss_feat_get_num_ovls(); ++i)
|
||||
dispc_ovl_set_burst_size(i, burst_size);
|
||||
if (dispc.feat->has_writeback)
|
||||
dispc_ovl_set_burst_size(OMAP_DSS_WB, burst_size);
|
||||
}
|
||||
|
||||
static u32 dispc_ovl_get_burst_size(enum omap_plane plane)
|
||||
|
@ -1196,6 +1212,17 @@ static void dispc_init_fifos(void)
|
|||
|
||||
dispc_ovl_set_fifo_threshold(i, low, high);
|
||||
}
|
||||
|
||||
if (dispc.feat->has_writeback) {
|
||||
u32 low, high;
|
||||
const bool use_fifomerge = false;
|
||||
const bool manual_update = false;
|
||||
|
||||
dispc_ovl_compute_fifo_thresholds(OMAP_DSS_WB, &low, &high,
|
||||
use_fifomerge, manual_update);
|
||||
|
||||
dispc_ovl_set_fifo_threshold(OMAP_DSS_WB, low, high);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 dispc_ovl_get_fifo_size(enum omap_plane plane)
|
||||
|
@ -1248,7 +1275,6 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high)
|
|||
plane != OMAP_DSS_WB)
|
||||
dispc_write_reg(DISPC_OVL_PRELOAD(plane), min(high, 0xfffu));
|
||||
}
|
||||
EXPORT_SYMBOL(dispc_ovl_set_fifo_threshold);
|
||||
|
||||
void dispc_enable_fifomerge(bool enable)
|
||||
{
|
||||
|
@ -1307,7 +1333,6 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
|
|||
*fifo_high = total_fifo_size - buf_unit;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(dispc_ovl_compute_fifo_thresholds);
|
||||
|
||||
static void dispc_ovl_set_mflag(enum omap_plane plane, bool enable)
|
||||
{
|
||||
|
@ -1364,6 +1389,25 @@ static void dispc_init_mflag(void)
|
|||
|
||||
dispc_ovl_set_mflag_threshold(i, low, high);
|
||||
}
|
||||
|
||||
if (dispc.feat->has_writeback) {
|
||||
u32 size = dispc_ovl_get_fifo_size(OMAP_DSS_WB);
|
||||
u32 unit = dss_feat_get_buffer_size_unit();
|
||||
u32 low, high;
|
||||
|
||||
dispc_ovl_set_mflag(OMAP_DSS_WB, true);
|
||||
|
||||
/*
|
||||
* Simulation team suggests below thesholds:
|
||||
* HT = fifosize * 5 / 8;
|
||||
* LT = fifosize * 4 / 8;
|
||||
*/
|
||||
|
||||
low = size * 4 / 8 / unit;
|
||||
high = size * 5 / 8 / unit;
|
||||
|
||||
dispc_ovl_set_mflag_threshold(OMAP_DSS_WB, low, high);
|
||||
}
|
||||
}
|
||||
|
||||
static void dispc_ovl_set_fir(enum omap_plane plane,
|
||||
|
@ -2438,7 +2482,7 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk,
|
|||
if (width == out_width && height == out_height)
|
||||
return 0;
|
||||
|
||||
if (pclk == 0 || mgr_timings->pixelclock == 0) {
|
||||
if (!mem_to_mem && (pclk == 0 || mgr_timings->pixelclock == 0)) {
|
||||
DSSERR("cannot calculate scaling settings: pclk is zero\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -2816,8 +2860,25 @@ int dispc_wb_setup(const struct omap_dss_writeback_info *wi,
|
|||
l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
|
||||
l = FLD_MOD(l, truncation, 10, 10); /* TRUNCATIONENABLE */
|
||||
l = FLD_MOD(l, mem_to_mem, 19, 19); /* WRITEBACKMODE */
|
||||
if (mem_to_mem)
|
||||
l = FLD_MOD(l, 1, 26, 24); /* CAPTUREMODE */
|
||||
else
|
||||
l = FLD_MOD(l, 0, 26, 24); /* CAPTUREMODE */
|
||||
dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l);
|
||||
|
||||
if (mem_to_mem) {
|
||||
/* WBDELAYCOUNT */
|
||||
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 7, 0);
|
||||
} else {
|
||||
int wbdelay;
|
||||
|
||||
wbdelay = min(mgr_timings->vfp + mgr_timings->vsw +
|
||||
mgr_timings->vbp, 255);
|
||||
|
||||
/* WBDELAYCOUNT */
|
||||
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), wbdelay, 7, 0);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -2896,7 +2957,7 @@ static void dispc_mgr_set_lcd_type_tft(enum omap_channel channel)
|
|||
mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1);
|
||||
}
|
||||
|
||||
void dispc_set_loadmode(enum omap_dss_load_mode mode)
|
||||
static void dispc_set_loadmode(enum omap_dss_load_mode mode)
|
||||
{
|
||||
REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1);
|
||||
}
|
||||
|
@ -3163,6 +3224,10 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
|
|||
FLD_VAL(hs, 13, 13) |
|
||||
FLD_VAL(vs, 12, 12);
|
||||
|
||||
/* always set ALIGN bit when available */
|
||||
if (dispc.feat->supports_sync_align)
|
||||
l |= (1 << 18);
|
||||
|
||||
dispc_write_reg(DISPC_POL_FREQ(channel), l);
|
||||
|
||||
if (dispc.syscon_pol) {
|
||||
|
@ -3220,7 +3285,7 @@ void dispc_mgr_set_timings(enum omap_channel channel,
|
|||
|
||||
DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt);
|
||||
} else {
|
||||
if (t.interlace == true)
|
||||
if (t.interlace)
|
||||
t.y_res /= 2;
|
||||
}
|
||||
|
||||
|
@ -3237,7 +3302,7 @@ static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div,
|
|||
dispc_write_reg(DISPC_DIVISORo(channel),
|
||||
FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0));
|
||||
|
||||
if (dss_has_feature(FEAT_CORE_CLK_DIV) == false &&
|
||||
if (!dss_has_feature(FEAT_CORE_CLK_DIV) &&
|
||||
channel == OMAP_DSS_CHANNEL_LCD)
|
||||
dispc.core_clk_rate = dispc_fclk_rate() / lck_div;
|
||||
}
|
||||
|
@ -3251,7 +3316,7 @@ static void dispc_mgr_get_lcd_divisor(enum omap_channel channel, int *lck_div,
|
|||
*pck_div = FLD_GET(l, 7, 0);
|
||||
}
|
||||
|
||||
unsigned long dispc_fclk_rate(void)
|
||||
static unsigned long dispc_fclk_rate(void)
|
||||
{
|
||||
struct dss_pll *pll;
|
||||
unsigned long r = 0;
|
||||
|
@ -3282,7 +3347,7 @@ unsigned long dispc_fclk_rate(void)
|
|||
return r;
|
||||
}
|
||||
|
||||
unsigned long dispc_mgr_lclk_rate(enum omap_channel channel)
|
||||
static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel)
|
||||
{
|
||||
struct dss_pll *pll;
|
||||
int lcd;
|
||||
|
@ -3323,7 +3388,7 @@ unsigned long dispc_mgr_lclk_rate(enum omap_channel channel)
|
|||
}
|
||||
}
|
||||
|
||||
unsigned long dispc_mgr_pclk_rate(enum omap_channel channel)
|
||||
static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel)
|
||||
{
|
||||
unsigned long r;
|
||||
|
||||
|
@ -3348,7 +3413,7 @@ void dispc_set_tv_pclk(unsigned long pclk)
|
|||
dispc.tv_pclk_rate = pclk;
|
||||
}
|
||||
|
||||
unsigned long dispc_core_clk_rate(void)
|
||||
static unsigned long dispc_core_clk_rate(void)
|
||||
{
|
||||
return dispc.core_clk_rate;
|
||||
}
|
||||
|
@ -3448,6 +3513,7 @@ static void dispc_dump_regs(struct seq_file *s)
|
|||
[OMAP_DSS_VIDEO1] = "VID1",
|
||||
[OMAP_DSS_VIDEO2] = "VID2",
|
||||
[OMAP_DSS_VIDEO3] = "VID3",
|
||||
[OMAP_DSS_WB] = "WB",
|
||||
};
|
||||
const char **p_names;
|
||||
|
||||
|
@ -3554,6 +3620,35 @@ static void dispc_dump_regs(struct seq_file *s)
|
|||
DUMPREG(i, DISPC_OVL_ATTRIBUTES2);
|
||||
}
|
||||
|
||||
if (dispc.feat->has_writeback) {
|
||||
i = OMAP_DSS_WB;
|
||||
DUMPREG(i, DISPC_OVL_BA0);
|
||||
DUMPREG(i, DISPC_OVL_BA1);
|
||||
DUMPREG(i, DISPC_OVL_SIZE);
|
||||
DUMPREG(i, DISPC_OVL_ATTRIBUTES);
|
||||
DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD);
|
||||
DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS);
|
||||
DUMPREG(i, DISPC_OVL_ROW_INC);
|
||||
DUMPREG(i, DISPC_OVL_PIXEL_INC);
|
||||
|
||||
if (dss_has_feature(FEAT_MFLAG))
|
||||
DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD);
|
||||
|
||||
DUMPREG(i, DISPC_OVL_FIR);
|
||||
DUMPREG(i, DISPC_OVL_PICTURE_SIZE);
|
||||
DUMPREG(i, DISPC_OVL_ACCU0);
|
||||
DUMPREG(i, DISPC_OVL_ACCU1);
|
||||
if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
|
||||
DUMPREG(i, DISPC_OVL_BA0_UV);
|
||||
DUMPREG(i, DISPC_OVL_BA1_UV);
|
||||
DUMPREG(i, DISPC_OVL_FIR2);
|
||||
DUMPREG(i, DISPC_OVL_ACCU2_0);
|
||||
DUMPREG(i, DISPC_OVL_ACCU2_1);
|
||||
}
|
||||
if (dss_has_feature(FEAT_ATTR2))
|
||||
DUMPREG(i, DISPC_OVL_ATTRIBUTES2);
|
||||
}
|
||||
|
||||
#undef DISPC_REG
|
||||
#undef DUMPREG
|
||||
|
||||
|
@ -3854,6 +3949,8 @@ static const struct dispc_features omap44xx_dispc_feats = {
|
|||
.num_fifos = 5,
|
||||
.gfx_fifo_workaround = true,
|
||||
.set_max_preload = true,
|
||||
.supports_sync_align = true,
|
||||
.has_writeback = true,
|
||||
};
|
||||
|
||||
static const struct dispc_features omap54xx_dispc_feats = {
|
||||
|
@ -3875,6 +3972,8 @@ static const struct dispc_features omap54xx_dispc_feats = {
|
|||
.gfx_fifo_workaround = true,
|
||||
.mstandby_workaround = true,
|
||||
.set_max_preload = true,
|
||||
.supports_sync_align = true,
|
||||
.has_writeback = true,
|
||||
};
|
||||
|
||||
static int dispc_init_features(struct platform_device *pdev)
|
|
@ -908,6 +908,8 @@ static inline u16 DISPC_MFLAG_THRESHOLD_OFFSET(enum omap_plane plane)
|
|||
return 0x0868;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x086c;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0870;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
|
@ -395,7 +395,7 @@ static int dpi_display_enable(struct omap_dss_device *dssdev)
|
|||
goto err_no_reg;
|
||||
}
|
||||
|
||||
if (out == NULL || out->manager == NULL) {
|
||||
if (out->manager == NULL) {
|
||||
DSSERR("failed to enable display: no output/manager\n");
|
||||
r = -ENODEV;
|
||||
goto err_no_out_mgr;
|
|
@ -144,7 +144,7 @@ struct dsi_reg { u16 module; u16 idx; };
|
|||
#define DSI_IRQ_TA_TIMEOUT (1 << 20)
|
||||
#define DSI_IRQ_ERROR_MASK \
|
||||
(DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \
|
||||
DSI_IRQ_TA_TIMEOUT | DSI_IRQ_SYNC_LOST)
|
||||
DSI_IRQ_TA_TIMEOUT)
|
||||
#define DSI_IRQ_CHANNEL_MASK 0xf
|
||||
|
||||
/* Virtual channel interrupts */
|
||||
|
@ -3833,7 +3833,7 @@ static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
|
|||
u16 word_count;
|
||||
int r;
|
||||
|
||||
if (out == NULL || out->manager == NULL) {
|
||||
if (out->manager == NULL) {
|
||||
DSSERR("failed to enable display: no output/manager\n");
|
||||
return -ENODEV;
|
||||
}
|
|
@ -1264,12 +1264,18 @@ static int dss_runtime_suspend(struct device *dev)
|
|||
{
|
||||
dss_save_context();
|
||||
dss_set_min_bus_tput(dev, 0);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dss_runtime_resume(struct device *dev)
|
||||
{
|
||||
int r;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
/*
|
||||
* Set an arbitrarily high tput request to ensure OPP100.
|
||||
* What we should really do is to make a request to stay in OPP100,
|
|
@ -343,7 +343,8 @@ u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt);
|
|||
#else
|
||||
static inline u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
|
||||
{
|
||||
WARN("%s: DSI not compiled in, returning pixel_size as 0\n", __func__);
|
||||
WARN(1, "%s: DSI not compiled in, returning pixel_size as 0\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -378,7 +379,6 @@ void dispc_lcd_enable_signal(bool enable);
|
|||
void dispc_pck_free_enable(bool enable);
|
||||
void dispc_enable_fifomerge(bool enable);
|
||||
void dispc_enable_gamma_table(bool enable);
|
||||
void dispc_set_loadmode(enum omap_dss_load_mode mode);
|
||||
|
||||
typedef bool (*dispc_div_calc_func)(int lckd, int pckd, unsigned long lck,
|
||||
unsigned long pck, void *data);
|
||||
|
@ -388,7 +388,6 @@ bool dispc_div_calc(unsigned long dispc,
|
|||
|
||||
bool dispc_mgr_timings_ok(enum omap_channel channel,
|
||||
const struct omap_video_timings *timings);
|
||||
unsigned long dispc_fclk_rate(void);
|
||||
int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
|
||||
struct dispc_clock_info *cinfo);
|
||||
|
||||
|
@ -398,9 +397,6 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
|
|||
u32 *fifo_low, u32 *fifo_high, bool use_fifomerge,
|
||||
bool manual_update);
|
||||
|
||||
unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
|
||||
unsigned long dispc_mgr_pclk_rate(enum omap_channel channel);
|
||||
unsigned long dispc_core_clk_rate(void);
|
||||
void dispc_mgr_set_clock_div(enum omap_channel channel,
|
||||
const struct dispc_clock_info *cinfo);
|
||||
int dispc_mgr_get_clock_div(enum omap_channel channel,
|
|
@ -46,7 +46,6 @@ struct omap_dss_features {
|
|||
|
||||
const int num_mgrs;
|
||||
const int num_ovls;
|
||||
const int num_wbs;
|
||||
const enum omap_display_type *supported_displays;
|
||||
const enum omap_dss_output_id *supported_outputs;
|
||||
const enum omap_color_mode *supported_color_modes;
|
||||
|
@ -735,7 +734,6 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = {
|
|||
|
||||
.num_mgrs = 3,
|
||||
.num_ovls = 4,
|
||||
.num_wbs = 1,
|
||||
.supported_displays = omap4_dss_supported_displays,
|
||||
.supported_outputs = omap4_dss_supported_outputs,
|
||||
.supported_color_modes = omap4_dss_supported_color_modes,
|
||||
|
@ -757,7 +755,6 @@ static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = {
|
|||
|
||||
.num_mgrs = 3,
|
||||
.num_ovls = 4,
|
||||
.num_wbs = 1,
|
||||
.supported_displays = omap4_dss_supported_displays,
|
||||
.supported_outputs = omap4_dss_supported_outputs,
|
||||
.supported_color_modes = omap4_dss_supported_color_modes,
|
||||
|
@ -779,7 +776,6 @@ static const struct omap_dss_features omap4_dss_features = {
|
|||
|
||||
.num_mgrs = 3,
|
||||
.num_ovls = 4,
|
||||
.num_wbs = 1,
|
||||
.supported_displays = omap4_dss_supported_displays,
|
||||
.supported_outputs = omap4_dss_supported_outputs,
|
||||
.supported_color_modes = omap4_dss_supported_color_modes,
|
||||
|
@ -825,11 +821,6 @@ int dss_feat_get_num_ovls(void)
|
|||
}
|
||||
EXPORT_SYMBOL(dss_feat_get_num_ovls);
|
||||
|
||||
int dss_feat_get_num_wbs(void)
|
||||
{
|
||||
return omap_current_dss_features->num_wbs;
|
||||
}
|
||||
|
||||
unsigned long dss_feat_get_param_min(enum dss_range_param param)
|
||||
{
|
||||
return omap_current_dss_features->dss_params[param].min;
|
||||
|
@ -844,13 +835,11 @@ enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel
|
|||
{
|
||||
return omap_current_dss_features->supported_displays[channel];
|
||||
}
|
||||
EXPORT_SYMBOL(dss_feat_get_supported_displays);
|
||||
|
||||
enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel)
|
||||
{
|
||||
return omap_current_dss_features->supported_outputs[channel];
|
||||
}
|
||||
EXPORT_SYMBOL(dss_feat_get_supported_outputs);
|
||||
|
||||
enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane)
|
||||
{
|
|
@ -86,7 +86,6 @@ enum dss_range_param {
|
|||
};
|
||||
|
||||
/* DSS Feature Functions */
|
||||
int dss_feat_get_num_wbs(void);
|
||||
unsigned long dss_feat_get_param_min(enum dss_range_param param);
|
||||
unsigned long dss_feat_get_param_max(enum dss_range_param param);
|
||||
enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane);
|
||||
|
@ -102,4 +101,8 @@ bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type);
|
|||
bool dss_has_feature(enum dss_feat_id id);
|
||||
void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end);
|
||||
void dss_features_init(enum omapdss_version version);
|
||||
|
||||
enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel);
|
||||
enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel);
|
||||
|
||||
#endif
|
|
@ -343,7 +343,7 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
|
|||
|
||||
mutex_lock(&hdmi.lock);
|
||||
|
||||
if (out == NULL || out->manager == NULL) {
|
||||
if (out->manager == NULL) {
|
||||
DSSERR("failed to enable display: no output/manager\n");
|
||||
r = -ENODEV;
|
||||
goto err0;
|
|
@ -373,7 +373,7 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
|
|||
|
||||
mutex_lock(&hdmi.lock);
|
||||
|
||||
if (out == NULL || out->manager == NULL) {
|
||||
if (out->manager == NULL) {
|
||||
DSSERR("failed to enable display: no output/manager\n");
|
||||
r = -ENODEV;
|
||||
goto err0;
|
|
@ -210,7 +210,7 @@ static int dss_mgr_check_lcd_config(struct omap_overlay_manager *mgr,
|
|||
return -EINVAL;
|
||||
|
||||
/* fifohandcheck should be used only with stallmode */
|
||||
if (stallmode == false && fifohandcheck == true)
|
||||
if (!stallmode && fifohandcheck)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
|
@ -199,10 +199,8 @@ static int __init omapdss_boot_init(void)
|
|||
omapdss_walk_device(dss, true);
|
||||
|
||||
for_each_available_child_of_node(dss, child) {
|
||||
if (!of_find_property(child, "compatible", NULL)) {
|
||||
of_node_put(child);
|
||||
if (!of_find_property(child, "compatible", NULL))
|
||||
continue;
|
||||
}
|
||||
|
||||
omapdss_walk_device(child, true);
|
||||
}
|
|
@ -880,7 +880,7 @@ static int rfbi_display_enable(struct omap_dss_device *dssdev)
|
|||
struct omap_dss_device *out = &rfbi.output;
|
||||
int r;
|
||||
|
||||
if (out == NULL || out->manager == NULL) {
|
||||
if (out->manager == NULL) {
|
||||
DSSERR("failed to enable display: no output/manager\n");
|
||||
return -ENODEV;
|
||||
}
|
|
@ -136,7 +136,7 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
|
|||
unsigned long pck;
|
||||
int r;
|
||||
|
||||
if (out == NULL || out->manager == NULL) {
|
||||
if (out->manager == NULL) {
|
||||
DSSERR("failed to enable display: no output/manager\n");
|
||||
return -ENODEV;
|
||||
}
|
|
@ -515,7 +515,7 @@ static int venc_display_enable(struct omap_dss_device *dssdev)
|
|||
|
||||
mutex_lock(&venc.venc_lock);
|
||||
|
||||
if (out == NULL || out->manager == NULL) {
|
||||
if (out->manager == NULL) {
|
||||
DSSERR("Failed to enable display: no output/manager\n");
|
||||
r = -ENODEV;
|
||||
goto err0;
|
|
@ -5,9 +5,9 @@ config VIDEO_OMAP2_VOUT
|
|||
tristate "OMAP2/OMAP3 V4L2-Display driver"
|
||||
depends on MMU
|
||||
depends on ARCH_OMAP2 || ARCH_OMAP3
|
||||
depends on FB_OMAP2
|
||||
select VIDEOBUF_GEN
|
||||
select VIDEOBUF_DMA_CONTIG
|
||||
select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS
|
||||
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
|
||||
select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB
|
||||
select FRAME_VECTOR
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
menuconfig FB
|
||||
tristate "Support for frame buffer devices"
|
||||
select FB_CMDLINE
|
||||
select FB_NOTIFY
|
||||
---help---
|
||||
The frame buffer device provides an abstraction for the graphics
|
||||
hardware. It represents the frame buffer of some video hardware and
|
||||
|
@ -56,6 +57,9 @@ config FIRMWARE_EDID
|
|||
config FB_CMDLINE
|
||||
bool
|
||||
|
||||
config FB_NOTIFY
|
||||
bool
|
||||
|
||||
config FB_DDC
|
||||
tristate
|
||||
depends on FB
|
||||
|
@ -1506,6 +1510,7 @@ config FB_SIS
|
|||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
select FB_BOOT_VESA_SUPPORT if FB_SIS = y
|
||||
select FB_SIS_300 if !FB_SIS_315
|
||||
help
|
||||
This is the frame buffer device driver for the SiS 300, 315, 330
|
||||
and 340 series as well as XGI V3XT, V5, V8, Z7 graphics chipsets.
|
||||
|
@ -1880,6 +1885,8 @@ config FB_PXA
|
|||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
select VIDEOMODE_HELPERS if OF
|
||||
select FB_MODE_HELPERS if OF
|
||||
---help---
|
||||
Frame buffer driver for the built-in LCD controller in the Intel
|
||||
PXA2x0 processor.
|
||||
|
@ -1990,16 +1997,6 @@ config FB_SH_MOBILE_LCDC
|
|||
---help---
|
||||
Frame buffer driver for the on-chip SH-Mobile LCD controller.
|
||||
|
||||
config FB_SH_MOBILE_HDMI
|
||||
tristate "SuperH Mobile HDMI controller support"
|
||||
depends on FB_SH_MOBILE_LCDC
|
||||
select FB_MODE_HELPERS
|
||||
select SOUND
|
||||
select SND
|
||||
select SND_SOC
|
||||
---help---
|
||||
Driver for the on-chip SH-Mobile HDMI controller.
|
||||
|
||||
config FB_TMIO
|
||||
tristate "Toshiba Mobile IO FrameBuffer support"
|
||||
depends on FB && (MFD_TMIO || COMPILE_TEST)
|
||||
|
|
|
@ -118,7 +118,6 @@ obj-$(CONFIG_FB_UDL) += udlfb.o
|
|||
obj-$(CONFIG_FB_SMSCUFX) += smscufx.o
|
||||
obj-$(CONFIG_FB_XILINX) += xilinxfb.o
|
||||
obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o
|
||||
obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o
|
||||
obj-$(CONFIG_FB_SH_MOBILE_MERAM) += sh_mobile_meram.o
|
||||
obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
|
||||
obj-$(CONFIG_FB_OMAP) += omap/
|
||||
|
|
|
@ -773,9 +773,7 @@ static void auok190x_recover(struct auok190xfb_par *par)
|
|||
/*
|
||||
* Power-management
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int auok190x_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused auok190x_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct fb_info *info = platform_get_drvdata(pdev);
|
||||
|
@ -822,7 +820,7 @@ finish:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int auok190x_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused auok190x_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct fb_info *info = platform_get_drvdata(pdev);
|
||||
|
@ -856,7 +854,7 @@ static int auok190x_runtime_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int auok190x_suspend(struct device *dev)
|
||||
static int __maybe_unused auok190x_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct fb_info *info = platform_get_drvdata(pdev);
|
||||
|
@ -896,7 +894,7 @@ static int auok190x_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int auok190x_resume(struct device *dev)
|
||||
static int __maybe_unused auok190x_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct fb_info *info = platform_get_drvdata(pdev);
|
||||
|
@ -933,7 +931,6 @@ static int auok190x_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct dev_pm_ops auok190x_pm = {
|
||||
SET_RUNTIME_PM_OPS(auok190x_runtime_suspend, auok190x_runtime_resume,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
obj-y += fb_notify.o
|
||||
obj-$(CONFIG_FB_CMDLINE) += fb_cmdline.o
|
||||
obj-$(CONFIG_FB_NOTIFY) += fb_notify.o
|
||||
obj-$(CONFIG_FB) += fb.o
|
||||
fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
|
||||
modedb.o fbcvt.o
|
||||
|
|
|
@ -1608,6 +1608,11 @@ static int do_remove_conflicting_framebuffers(struct apertures_struct *a,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool lockless_register_fb;
|
||||
module_param_named_unsafe(lockless_register_fb, lockless_register_fb, bool, 0400);
|
||||
MODULE_PARM_DESC(lockless_register_fb,
|
||||
"Lockless framebuffer registration for debugging [default=off]");
|
||||
|
||||
static int do_register_framebuffer(struct fb_info *fb_info)
|
||||
{
|
||||
int i, ret;
|
||||
|
@ -1675,14 +1680,17 @@ static int do_register_framebuffer(struct fb_info *fb_info)
|
|||
registered_fb[i] = fb_info;
|
||||
|
||||
event.info = fb_info;
|
||||
if (!lockless_register_fb)
|
||||
console_lock();
|
||||
if (!lock_fb_info(fb_info)) {
|
||||
if (!lockless_register_fb)
|
||||
console_unlock();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
|
||||
unlock_fb_info(fb_info);
|
||||
if (!lockless_register_fb)
|
||||
console_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -208,7 +208,7 @@ static void gx1_set_hw_palette_reg(struct fb_info *info, unsigned regno,
|
|||
writel(val, par->dc_regs + DC_PAL_DATA);
|
||||
}
|
||||
|
||||
struct geode_dc_ops gx1_dc_ops = {
|
||||
const struct geode_dc_ops gx1_dc_ops = {
|
||||
.set_mode = gx1_set_mode,
|
||||
.set_palette_reg = gx1_set_hw_palette_reg,
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
unsigned gx1_gx_base(void);
|
||||
int gx1_frame_buffer_size(void);
|
||||
|
||||
extern struct geode_dc_ops gx1_dc_ops;
|
||||
extern const struct geode_dc_ops gx1_dc_ops;
|
||||
|
||||
/* GX1 configuration I/O registers */
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@ struct geodefb_par {
|
|||
int panel_y;
|
||||
void __iomem *dc_regs;
|
||||
void __iomem *vid_regs;
|
||||
struct geode_dc_ops *dc_ops;
|
||||
struct geode_vid_ops *vid_ops;
|
||||
const struct geode_dc_ops *dc_ops;
|
||||
const struct geode_vid_ops *vid_ops;
|
||||
};
|
||||
|
||||
#endif /* !__GEODEFB_H__ */
|
||||
|
|
|
@ -186,7 +186,7 @@ static int cs5530_blank_display(struct fb_info *info, int blank_mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct geode_vid_ops cs5530_vid_ops = {
|
||||
const struct geode_vid_ops cs5530_vid_ops = {
|
||||
.set_dclk = cs5530_set_dclk_frequency,
|
||||
.configure_display = cs5530_configure_display,
|
||||
.blank_display = cs5530_blank_display,
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef __VIDEO_CS5530_H__
|
||||
#define __VIDEO_CS5530_H__
|
||||
|
||||
extern struct geode_vid_ops cs5530_vid_ops;
|
||||
extern const struct geode_vid_ops cs5530_vid_ops;
|
||||
|
||||
/* CS5530 Video device registers */
|
||||
|
||||
|
|
|
@ -346,11 +346,10 @@ static void i740_calc_vclk(u32 freq, struct i740fb_par *par)
|
|||
const u32 err_target = freq / (1000 * I740_RFREQ / I740_FFIX);
|
||||
u32 err_best = 512 * I740_FFIX;
|
||||
u32 f_err, f_vco;
|
||||
int m_best = 0, n_best = 0, p_best = 0, d_best = 0;
|
||||
int m_best = 0, n_best = 0, p_best = 0;
|
||||
int m, n;
|
||||
|
||||
p_best = min(15, ilog2(I740_MAX_VCO_FREQ / (freq / I740_RFREQ_FIX)));
|
||||
d_best = 0;
|
||||
f_vco = (freq * (1 << p_best)) / I740_RFREQ_FIX;
|
||||
freq = freq / I740_RFREQ_FIX;
|
||||
|
||||
|
@ -363,7 +362,7 @@ static void i740_calc_vclk(u32 freq, struct i740fb_par *par)
|
|||
m = 3;
|
||||
|
||||
{
|
||||
u32 f_out = (((m * I740_REF_FREQ * (4 << 2 * d_best))
|
||||
u32 f_out = (((m * I740_REF_FREQ * 4)
|
||||
/ n) + ((1 << p_best) / 2)) / (1 << p_best);
|
||||
|
||||
f_err = (freq - f_out);
|
||||
|
@ -386,8 +385,7 @@ static void i740_calc_vclk(u32 freq, struct i740fb_par *par)
|
|||
par->video_clk2_n = (n_best - 2) & 0xFF;
|
||||
par->video_clk2_mn_msbs = ((((n_best - 2) >> 4) & VCO_N_MSBS)
|
||||
| (((m_best - 2) >> 8) & VCO_M_MSBS));
|
||||
par->video_clk2_div_sel =
|
||||
((p_best << 4) | (d_best ? 4 : 0) | REF_DIV_1);
|
||||
par->video_clk2_div_sel = ((p_best << 4) | REF_DIV_1);
|
||||
}
|
||||
|
||||
static int i740fb_decode_var(const struct fb_var_screeninfo *var,
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
config OMAP2_VRFB
|
||||
bool
|
||||
|
||||
if ARCH_OMAP2PLUS
|
||||
|
||||
source "drivers/video/fbdev/omap2/dss/Kconfig"
|
||||
source "drivers/video/fbdev/omap2/omapfb/Kconfig"
|
||||
source "drivers/video/fbdev/omap2/displays-new/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
|
||||
|
||||
obj-y += dss/
|
||||
obj-y += displays-new/
|
||||
obj-$(CONFIG_FB_OMAP2) += omapfb/
|
||||
obj-y += omapfb/
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
config OMAP2_VRFB
|
||||
bool
|
||||
|
||||
menuconfig FB_OMAP2
|
||||
tristate "OMAP2+ frame buffer support"
|
||||
depends on FB && OMAP2_DSS && !DRM_OMAP
|
||||
depends on FB
|
||||
depends on DRM_OMAP = n
|
||||
|
||||
select FB_OMAP2_DSS
|
||||
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
|
@ -9,6 +14,8 @@ menuconfig FB_OMAP2
|
|||
help
|
||||
Frame buffer driver for OMAP2+ based boards.
|
||||
|
||||
if FB_OMAP2
|
||||
|
||||
config FB_OMAP2_DEBUG_SUPPORT
|
||||
bool "Debug support for OMAP2+ FB"
|
||||
default y
|
||||
|
@ -25,3 +32,8 @@ config FB_OMAP2_NUM_FBS
|
|||
help
|
||||
Select the number of framebuffers created. OMAP2/3 has 3 overlays
|
||||
so normally this would be 3.
|
||||
|
||||
source "drivers/video/fbdev/omap2/omapfb/dss/Kconfig"
|
||||
source "drivers/video/fbdev/omap2/omapfb/displays/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
|
||||
obj-y += dss/
|
||||
obj-y += displays/
|
||||
obj-$(CONFIG_FB_OMAP2) += omapfb.o
|
||||
omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
menu "OMAPFB Panel and Encoder Drivers"
|
||||
depends on FB_OMAP2_DSS
|
||||
|
||||
config FB_OMAP2_ENCODER_OPA362
|
||||
tristate "OPA362 external analog amplifier"
|
||||
help
|
||||
Driver for OPA362 external analog TV amplifier controlled
|
||||
through a GPIO.
|
||||
|
||||
config FB_OMAP2_ENCODER_TFP410
|
||||
tristate "TFP410 DPI to DVI Encoder"
|
||||
help
|
||||
Driver for TFP410 DPI to DVI encoder.
|
||||
|
||||
config FB_OMAP2_ENCODER_TPD12S015
|
||||
tristate "TPD12S015 HDMI ESD protection and level shifter"
|
||||
help
|
||||
Driver for TPD12S015, which offers HDMI ESD protection and level
|
||||
shifting.
|
||||
|
||||
config FB_OMAP2_CONNECTOR_DVI
|
||||
tristate "DVI Connector"
|
||||
depends on I2C
|
||||
help
|
||||
Driver for a generic DVI connector.
|
||||
|
||||
config FB_OMAP2_CONNECTOR_HDMI
|
||||
tristate "HDMI Connector"
|
||||
help
|
||||
Driver for a generic HDMI connector.
|
||||
|
||||
config FB_OMAP2_CONNECTOR_ANALOG_TV
|
||||
tristate "Analog TV Connector"
|
||||
help
|
||||
Driver for a generic analog TV connector.
|
||||
|
||||
config FB_OMAP2_PANEL_DPI
|
||||
tristate "Generic DPI panel"
|
||||
help
|
||||
Driver for generic DPI panels.
|
||||
|
||||
config FB_OMAP2_PANEL_DSI_CM
|
||||
tristate "Generic DSI Command Mode Panel"
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Driver for generic DSI command mode panels.
|
||||
|
||||
config FB_OMAP2_PANEL_SONY_ACX565AKM
|
||||
tristate "ACX565AKM Panel"
|
||||
depends on SPI && BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
This is the LCD panel used on Nokia N900
|
||||
|
||||
config FB_OMAP2_PANEL_LGPHILIPS_LB035Q02
|
||||
tristate "LG.Philips LB035Q02 LCD Panel"
|
||||
depends on SPI
|
||||
help
|
||||
LCD Panel used on the Gumstix Overo Palo35
|
||||
|
||||
config FB_OMAP2_PANEL_SHARP_LS037V7DW01
|
||||
tristate "Sharp LS037V7DW01 LCD Panel"
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
LCD Panel used in TI's SDP3430 and EVM boards
|
||||
|
||||
config FB_OMAP2_PANEL_TPO_TD028TTEC1
|
||||
tristate "TPO TD028TTEC1 LCD Panel"
|
||||
depends on SPI
|
||||
help
|
||||
LCD panel used in Openmoko.
|
||||
|
||||
config FB_OMAP2_PANEL_TPO_TD043MTEA1
|
||||
tristate "TPO TD043MTEA1 LCD Panel"
|
||||
depends on SPI
|
||||
help
|
||||
LCD Panel used in OMAP3 Pandora
|
||||
|
||||
config FB_OMAP2_PANEL_NEC_NL8048HL11
|
||||
tristate "NEC NL8048HL11 Panel"
|
||||
depends on SPI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
This NEC NL8048HL11 panel is TFT LCD used in the
|
||||
Zoom2/3/3630 sdp boards.
|
||||
|
||||
endmenu
|
|
@ -0,0 +1,14 @@
|
|||
obj-$(CONFIG_FB_OMAP2_ENCODER_OPA362) += encoder-opa362.o
|
||||
obj-$(CONFIG_FB_OMAP2_ENCODER_TFP410) += encoder-tfp410.o
|
||||
obj-$(CONFIG_FB_OMAP2_ENCODER_TPD12S015) += encoder-tpd12s015.o
|
||||
obj-$(CONFIG_FB_OMAP2_CONNECTOR_DVI) += connector-dvi.o
|
||||
obj-$(CONFIG_FB_OMAP2_CONNECTOR_HDMI) += connector-hdmi.o
|
||||
obj-$(CONFIG_FB_OMAP2_CONNECTOR_ANALOG_TV) += connector-analog-tv.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_DPI) += panel-dpi.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_DSI_CM) += panel-dsi-cm.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* Analog TV Connector driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
|
||||
enum omap_dss_venc_type connector_type;
|
||||
bool invert_polarity;
|
||||
};
|
||||
|
||||
static const struct omap_video_timings tvc_pal_timings = {
|
||||
.x_res = 720,
|
||||
.y_res = 574,
|
||||
.pixelclock = 13500000,
|
||||
.hsw = 64,
|
||||
.hfp = 12,
|
||||
.hbp = 68,
|
||||
.vsw = 5,
|
||||
.vfp = 5,
|
||||
.vbp = 41,
|
||||
|
||||
.interlace = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id tvc_of_match[];
|
||||
|
||||
struct tvc_of_data {
|
||||
enum omap_dss_venc_type connector_type;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int tvc_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(ddata->dev, "connect\n");
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.atv->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tvc_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(ddata->dev, "disconnect\n");
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.atv->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int tvc_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(ddata->dev, "enable\n");
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
in->ops.atv->set_timings(in, &ddata->timings);
|
||||
|
||||
if (!ddata->dev->of_node) {
|
||||
in->ops.atv->set_type(in, ddata->connector_type);
|
||||
|
||||
in->ops.atv->invert_vid_out_polarity(in,
|
||||
ddata->invert_polarity);
|
||||
}
|
||||
|
||||
r = in->ops.atv->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void tvc_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(ddata->dev, "disable\n");
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.atv->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void tvc_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.atv->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void tvc_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int tvc_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.atv->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static u32 tvc_get_wss(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.atv->get_wss(in);
|
||||
}
|
||||
|
||||
static int tvc_set_wss(struct omap_dss_device *dssdev, u32 wss)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.atv->set_wss(in, wss);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver tvc_driver = {
|
||||
.connect = tvc_connect,
|
||||
.disconnect = tvc_disconnect,
|
||||
|
||||
.enable = tvc_enable,
|
||||
.disable = tvc_disable,
|
||||
|
||||
.set_timings = tvc_set_timings,
|
||||
.get_timings = tvc_get_timings,
|
||||
.check_timings = tvc_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
|
||||
.get_wss = tvc_get_wss,
|
||||
.set_wss = tvc_set_wss,
|
||||
};
|
||||
|
||||
static int tvc_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct connector_atv_platform_data *pdata;
|
||||
struct omap_dss_device *in, *dssdev;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&pdev->dev, "Failed to find video source\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
ddata->connector_type = pdata->connector_type;
|
||||
ddata->invert_polarity = pdata->invert_polarity;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tvc_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tvc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
ddata->dev = &pdev->dev;
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = tvc_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = tvc_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->timings = tvc_pal_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->driver = &tvc_driver;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_VENC;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = tvc_pal_timings;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit tvc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(&ddata->dssdev);
|
||||
|
||||
tvc_disable(dssdev);
|
||||
tvc_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tvc_of_match[] = {
|
||||
{ .compatible = "omapdss,svideo-connector", },
|
||||
{ .compatible = "omapdss,composite-video-connector", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, tvc_of_match);
|
||||
|
||||
static struct platform_driver tvc_connector_driver = {
|
||||
.probe = tvc_probe,
|
||||
.remove = __exit_p(tvc_remove),
|
||||
.driver = {
|
||||
.name = "connector-analog-tv",
|
||||
.of_match_table = tvc_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(tvc_connector_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("Analog TV Connector driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Generic DVI Connector driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
static const struct omap_video_timings dvic_default_timings = {
|
||||
.x_res = 640,
|
||||
.y_res = 480,
|
||||
|
||||
.pixelclock = 23500000,
|
||||
|
||||
.hfp = 48,
|
||||
.hsw = 32,
|
||||
.hbp = 80,
|
||||
|
||||
.vfp = 3,
|
||||
.vsw = 4,
|
||||
.vbp = 7,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
};
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
|
||||
struct i2c_adapter *i2c_adapter;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int dvic_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dvi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dvic_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dvi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int dvic_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
in->ops.dvi->set_timings(in, &ddata->timings);
|
||||
|
||||
r = in->ops.dvi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dvic_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dvi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void dvic_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dvi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void dvic_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int dvic_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dvi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static int dvic_ddc_read(struct i2c_adapter *adapter,
|
||||
unsigned char *buf, u16 count, u8 offset)
|
||||
{
|
||||
int r, retries;
|
||||
|
||||
for (retries = 3; retries > 0; retries--) {
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = DDC_ADDR,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = &offset,
|
||||
}, {
|
||||
.addr = DDC_ADDR,
|
||||
.flags = I2C_M_RD,
|
||||
.len = count,
|
||||
.buf = buf,
|
||||
}
|
||||
};
|
||||
|
||||
r = i2c_transfer(adapter, msgs, 2);
|
||||
if (r == 2)
|
||||
return 0;
|
||||
|
||||
if (r != -EAGAIN)
|
||||
break;
|
||||
}
|
||||
|
||||
return r < 0 ? r : -EIO;
|
||||
}
|
||||
|
||||
static int dvic_read_edid(struct omap_dss_device *dssdev,
|
||||
u8 *edid, int len)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
int r, l, bytes_read;
|
||||
|
||||
if (!ddata->i2c_adapter)
|
||||
return -ENODEV;
|
||||
|
||||
l = min(EDID_LENGTH, len);
|
||||
r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
bytes_read = l;
|
||||
|
||||
/* if there are extensions, read second block */
|
||||
if (len > EDID_LENGTH && edid[0x7e] > 0) {
|
||||
l = min(EDID_LENGTH, len - EDID_LENGTH);
|
||||
|
||||
r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
|
||||
l, EDID_LENGTH);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
bytes_read += l;
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static bool dvic_detect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
unsigned char out;
|
||||
int r;
|
||||
|
||||
if (!ddata->i2c_adapter)
|
||||
return true;
|
||||
|
||||
r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0);
|
||||
|
||||
return r == 0;
|
||||
}
|
||||
|
||||
static struct omap_dss_driver dvic_driver = {
|
||||
.connect = dvic_connect,
|
||||
.disconnect = dvic_disconnect,
|
||||
|
||||
.enable = dvic_enable,
|
||||
.disable = dvic_disable,
|
||||
|
||||
.set_timings = dvic_set_timings,
|
||||
.get_timings = dvic_get_timings,
|
||||
.check_timings = dvic_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
|
||||
.read_edid = dvic_read_edid,
|
||||
.detect = dvic_detect,
|
||||
};
|
||||
|
||||
static int dvic_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct connector_dvi_platform_data *pdata;
|
||||
struct omap_dss_device *in, *dssdev;
|
||||
int i2c_bus_num;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
i2c_bus_num = pdata->i2c_bus_num;
|
||||
|
||||
if (i2c_bus_num != -1) {
|
||||
struct i2c_adapter *adapter;
|
||||
|
||||
adapter = i2c_get_adapter(i2c_bus_num);
|
||||
if (!adapter) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to get I2C adapter, bus %d\n",
|
||||
i2c_bus_num);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->i2c_adapter = adapter;
|
||||
}
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
i2c_put_adapter(ddata->i2c_adapter);
|
||||
|
||||
dev_err(&pdev->dev, "Failed to find video source\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dvic_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
struct device_node *adapter_node;
|
||||
struct i2c_adapter *adapter;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
|
||||
if (adapter_node) {
|
||||
adapter = of_get_i2c_adapter_by_node(adapter_node);
|
||||
if (adapter == NULL) {
|
||||
dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
|
||||
omap_dss_put_device(ddata->in);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->i2c_adapter = adapter;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dvic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = dvic_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = dvic_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->timings = dvic_default_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->driver = &dvic_driver;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DVI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = dvic_default_timings;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
|
||||
i2c_put_adapter(ddata->i2c_adapter);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit dvic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(&ddata->dssdev);
|
||||
|
||||
dvic_disable(dssdev);
|
||||
dvic_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
i2c_put_adapter(ddata->i2c_adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dvic_of_match[] = {
|
||||
{ .compatible = "omapdss,dvi-connector", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, dvic_of_match);
|
||||
|
||||
static struct platform_driver dvi_connector_driver = {
|
||||
.probe = dvic_probe,
|
||||
.remove = __exit_p(dvic_remove),
|
||||
.driver = {
|
||||
.name = "connector-dvi",
|
||||
.of_match_table = dvic_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dvi_connector_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("Generic DVI Connector driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* HDMI Connector driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
static const struct omap_video_timings hdmic_default_timings = {
|
||||
.x_res = 640,
|
||||
.y_res = 480,
|
||||
.pixelclock = 25175000,
|
||||
.hsw = 96,
|
||||
.hfp = 16,
|
||||
.hbp = 48,
|
||||
.vsw = 2,
|
||||
.vfp = 11,
|
||||
.vbp = 31,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
|
||||
.interlace = false,
|
||||
};
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
|
||||
int hpd_gpio;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int hdmic_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(ddata->dev, "connect\n");
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.hdmi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmic_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(ddata->dev, "disconnect\n");
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.hdmi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int hdmic_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(ddata->dev, "enable\n");
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
in->ops.hdmi->set_timings(in, &ddata->timings);
|
||||
|
||||
r = in->ops.hdmi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void hdmic_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(ddata->dev, "disable\n");
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.hdmi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void hdmic_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.hdmi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void hdmic_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int hdmic_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static int hdmic_read_edid(struct omap_dss_device *dssdev,
|
||||
u8 *edid, int len)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->read_edid(in, edid, len);
|
||||
}
|
||||
|
||||
static bool hdmic_detect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (gpio_is_valid(ddata->hpd_gpio))
|
||||
return gpio_get_value_cansleep(ddata->hpd_gpio);
|
||||
else
|
||||
return in->ops.hdmi->detect(in);
|
||||
}
|
||||
|
||||
static int hdmic_set_hdmi_mode(struct omap_dss_device *dssdev, bool hdmi_mode)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
|
||||
}
|
||||
|
||||
static int hdmic_set_infoframe(struct omap_dss_device *dssdev,
|
||||
const struct hdmi_avi_infoframe *avi)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->set_infoframe(in, avi);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver hdmic_driver = {
|
||||
.connect = hdmic_connect,
|
||||
.disconnect = hdmic_disconnect,
|
||||
|
||||
.enable = hdmic_enable,
|
||||
.disable = hdmic_disable,
|
||||
|
||||
.set_timings = hdmic_set_timings,
|
||||
.get_timings = hdmic_get_timings,
|
||||
.check_timings = hdmic_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
|
||||
.read_edid = hdmic_read_edid,
|
||||
.detect = hdmic_detect,
|
||||
.set_hdmi_mode = hdmic_set_hdmi_mode,
|
||||
.set_hdmi_infoframe = hdmic_set_infoframe,
|
||||
};
|
||||
|
||||
static int hdmic_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct connector_hdmi_platform_data *pdata;
|
||||
struct omap_dss_device *in, *dssdev;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
ddata->hpd_gpio = -ENODEV;
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&pdev->dev, "Failed to find video source\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmic_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
int gpio;
|
||||
|
||||
/* HPD GPIO */
|
||||
gpio = of_get_named_gpio(node, "hpd-gpios", 0);
|
||||
if (gpio_is_valid(gpio))
|
||||
ddata->hpd_gpio = gpio;
|
||||
else
|
||||
ddata->hpd_gpio = -ENODEV;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
ddata->dev = &pdev->dev;
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = hdmic_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = hdmic_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->hpd_gpio)) {
|
||||
r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
|
||||
GPIOF_DIR_IN, "hdmi_hpd");
|
||||
if (r)
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
ddata->timings = hdmic_default_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->driver = &hdmic_driver;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = hdmic_default_timings;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit hdmic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(&ddata->dssdev);
|
||||
|
||||
hdmic_disable(dssdev);
|
||||
hdmic_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id hdmic_of_match[] = {
|
||||
{ .compatible = "omapdss,hdmi-connector", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, hdmic_of_match);
|
||||
|
||||
static struct platform_driver hdmi_connector_driver = {
|
||||
.probe = hdmic_probe,
|
||||
.remove = __exit_p(hdmic_remove),
|
||||
.driver = {
|
||||
.name = "connector-hdmi",
|
||||
.of_match_table = hdmic_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(hdmi_connector_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("HDMI Connector driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* OPA362 analog video amplifier with output/power control
|
||||
*
|
||||
* Copyright (C) 2014 Golden Delicious Computers
|
||||
* Author: H. Nikolaus Schaller <hns@goldelico.com>
|
||||
*
|
||||
* based on encoder-tfp410
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct gpio_desc *enable_gpio;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int opa362_connect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(dssdev->dev, "connect\n");
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return -EBUSY;
|
||||
|
||||
r = in->ops.atv->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dst->src = dssdev;
|
||||
dssdev->dst = dst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void opa362_disconnect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(dssdev->dev, "disconnect\n");
|
||||
|
||||
WARN_ON(!omapdss_device_is_connected(dssdev));
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
WARN_ON(dst != dssdev->dst);
|
||||
if (dst != dssdev->dst)
|
||||
return;
|
||||
|
||||
dst->src = NULL;
|
||||
dssdev->dst = NULL;
|
||||
|
||||
in->ops.atv->disconnect(in, &ddata->dssdev);
|
||||
}
|
||||
|
||||
static int opa362_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(dssdev->dev, "enable\n");
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
in->ops.atv->set_timings(in, &ddata->timings);
|
||||
|
||||
r = in->ops.atv->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (ddata->enable_gpio)
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void opa362_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(dssdev->dev, "disable\n");
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (ddata->enable_gpio)
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
|
||||
|
||||
in->ops.atv->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void opa362_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(dssdev->dev, "set_timings\n");
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.atv->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void opa362_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
dev_dbg(dssdev->dev, "get_timings\n");
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int opa362_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(dssdev->dev, "check_timings\n");
|
||||
|
||||
return in->ops.atv->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static void opa362_set_type(struct omap_dss_device *dssdev,
|
||||
enum omap_dss_venc_type type)
|
||||
{
|
||||
/* we can only drive a COMPOSITE output */
|
||||
WARN_ON(type != OMAP_DSS_VENC_TYPE_COMPOSITE);
|
||||
|
||||
}
|
||||
|
||||
static const struct omapdss_atv_ops opa362_atv_ops = {
|
||||
.connect = opa362_connect,
|
||||
.disconnect = opa362_disconnect,
|
||||
|
||||
.enable = opa362_enable,
|
||||
.disable = opa362_disable,
|
||||
|
||||
.check_timings = opa362_check_timings,
|
||||
.set_timings = opa362_set_timings,
|
||||
.get_timings = opa362_get_timings,
|
||||
|
||||
.set_type = opa362_set_type,
|
||||
};
|
||||
|
||||
static int opa362_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
struct gpio_desc *gpio;
|
||||
int r;
|
||||
|
||||
dev_dbg(&pdev->dev, "probe\n");
|
||||
|
||||
if (node == NULL) {
|
||||
dev_err(&pdev->dev, "Unable to find device tree\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
ddata->enable_gpio = gpio;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->ops.atv = &opa362_atv_ops;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_VENC;
|
||||
dssdev->output_type = OMAP_DISPLAY_TYPE_VENC;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
|
||||
r = omapdss_register_output(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register output\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit opa362_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_output(&ddata->dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_enabled(dssdev));
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
opa362_disable(dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_connected(dssdev));
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
opa362_disconnect(dssdev, dssdev->dst);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id opa362_of_match[] = {
|
||||
{ .compatible = "omapdss,ti,opa362", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, opa362_of_match);
|
||||
|
||||
static struct platform_driver opa362_driver = {
|
||||
.probe = opa362_probe,
|
||||
.remove = __exit_p(opa362_remove),
|
||||
.driver = {
|
||||
.name = "amplifier-opa362",
|
||||
.of_match_table = opa362_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(opa362_driver);
|
||||
|
||||
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
|
||||
MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* TFP410 DPI-to-DVI encoder driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
int pd_gpio;
|
||||
int data_lines;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int tfp410_connect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return -EBUSY;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dst->src = dssdev;
|
||||
dssdev->dst = dst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tfp410_disconnect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
WARN_ON(!omapdss_device_is_connected(dssdev));
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
WARN_ON(dst != dssdev->dst);
|
||||
if (dst != dssdev->dst)
|
||||
return;
|
||||
|
||||
dst->src = NULL;
|
||||
dssdev->dst = NULL;
|
||||
|
||||
in->ops.dpi->disconnect(in, &ddata->dssdev);
|
||||
}
|
||||
|
||||
static int tfp410_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
in->ops.dpi->set_timings(in, &ddata->timings);
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (gpio_is_valid(ddata->pd_gpio))
|
||||
gpio_set_value_cansleep(ddata->pd_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tfp410_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (gpio_is_valid(ddata->pd_gpio))
|
||||
gpio_set_value_cansleep(ddata->pd_gpio, 0);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void tfp410_fix_timings(struct omap_video_timings *timings)
|
||||
{
|
||||
timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
|
||||
timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
|
||||
timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
static void tfp410_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
tfp410_fix_timings(timings);
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void tfp410_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int tfp410_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
tfp410_fix_timings(timings);
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static const struct omapdss_dvi_ops tfp410_dvi_ops = {
|
||||
.connect = tfp410_connect,
|
||||
.disconnect = tfp410_disconnect,
|
||||
|
||||
.enable = tfp410_enable,
|
||||
.disable = tfp410_disable,
|
||||
|
||||
.check_timings = tfp410_check_timings,
|
||||
.set_timings = tfp410_set_timings,
|
||||
.get_timings = tfp410_get_timings,
|
||||
};
|
||||
|
||||
static int tfp410_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct encoder_tfp410_platform_data *pdata;
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
ddata->pd_gpio = pdata->power_down_gpio;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&pdev->dev, "Failed to find video source\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tfp410_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
int gpio;
|
||||
|
||||
gpio = of_get_named_gpio(node, "powerdown-gpios", 0);
|
||||
|
||||
if (gpio_is_valid(gpio) || gpio == -ENOENT) {
|
||||
ddata->pd_gpio = gpio;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to parse PD gpio\n");
|
||||
return gpio;
|
||||
}
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tfp410_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = tfp410_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = tfp410_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->pd_gpio)) {
|
||||
r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "tfp410 PD");
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to request PD GPIO %d\n",
|
||||
ddata->pd_gpio);
|
||||
goto err_gpio;
|
||||
}
|
||||
}
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->ops.dvi = &tfp410_dvi_ops;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->output_type = OMAP_DISPLAY_TYPE_DVI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->phy.dpi.data_lines = ddata->data_lines;
|
||||
dssdev->port_num = 1;
|
||||
|
||||
r = omapdss_register_output(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register output\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_reg:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit tfp410_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_output(&ddata->dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_enabled(dssdev));
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
tfp410_disable(dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_connected(dssdev));
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
tfp410_disconnect(dssdev, dssdev->dst);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tfp410_of_match[] = {
|
||||
{ .compatible = "omapdss,ti,tfp410", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, tfp410_of_match);
|
||||
|
||||
static struct platform_driver tfp410_driver = {
|
||||
.probe = tfp410_probe,
|
||||
.remove = __exit_p(tfp410_remove),
|
||||
.driver = {
|
||||
.name = "tfp410",
|
||||
.of_match_table = tfp410_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(tfp410_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* TPD12S015 HDMI ESD protection & level shifter chip driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct gpio_desc *ct_cp_hpd_gpio;
|
||||
struct gpio_desc *ls_oe_gpio;
|
||||
struct gpio_desc *hpd_gpio;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int tpd_connect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
r = in->ops.hdmi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dst->src = dssdev;
|
||||
dssdev->dst = dst;
|
||||
|
||||
if (ddata->ct_cp_hpd_gpio) {
|
||||
gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
|
||||
/* DC-DC converter needs at max 300us to get to 90% of 5V */
|
||||
udelay(300);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpd_disconnect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
WARN_ON(dst != dssdev->dst);
|
||||
|
||||
if (dst != dssdev->dst)
|
||||
return;
|
||||
|
||||
gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
|
||||
|
||||
dst->src = NULL;
|
||||
dssdev->dst = NULL;
|
||||
|
||||
in->ops.hdmi->disconnect(in, &ddata->dssdev);
|
||||
}
|
||||
|
||||
static int tpd_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
|
||||
return 0;
|
||||
|
||||
in->ops.hdmi->set_timings(in, &ddata->timings);
|
||||
|
||||
r = in->ops.hdmi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void tpd_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
|
||||
return;
|
||||
|
||||
in->ops.hdmi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void tpd_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.hdmi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void tpd_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int tpd_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
r = in->ops.hdmi->check_timings(in, timings);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int tpd_read_edid(struct omap_dss_device *dssdev,
|
||||
u8 *edid, int len)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
|
||||
return -ENODEV;
|
||||
|
||||
gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
|
||||
|
||||
r = in->ops.hdmi->read_edid(in, edid, len);
|
||||
|
||||
gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool tpd_detect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
return gpiod_get_value_cansleep(ddata->hpd_gpio);
|
||||
}
|
||||
|
||||
static int tpd_set_infoframe(struct omap_dss_device *dssdev,
|
||||
const struct hdmi_avi_infoframe *avi)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->set_infoframe(in, avi);
|
||||
}
|
||||
|
||||
static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev,
|
||||
bool hdmi_mode)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
|
||||
}
|
||||
|
||||
static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
|
||||
.connect = tpd_connect,
|
||||
.disconnect = tpd_disconnect,
|
||||
|
||||
.enable = tpd_enable,
|
||||
.disable = tpd_disable,
|
||||
|
||||
.check_timings = tpd_check_timings,
|
||||
.set_timings = tpd_set_timings,
|
||||
.get_timings = tpd_get_timings,
|
||||
|
||||
.read_edid = tpd_read_edid,
|
||||
.detect = tpd_detect,
|
||||
.set_infoframe = tpd_set_infoframe,
|
||||
.set_hdmi_mode = tpd_set_hdmi_mode,
|
||||
};
|
||||
|
||||
static int tpd_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_dss_device *in, *dssdev;
|
||||
struct panel_drv_data *ddata;
|
||||
int r;
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
r = tpd_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
goto err_gpio;
|
||||
|
||||
ddata->ct_cp_hpd_gpio = gpio;
|
||||
|
||||
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
goto err_gpio;
|
||||
|
||||
ddata->ls_oe_gpio = gpio;
|
||||
|
||||
gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(gpio))
|
||||
goto err_gpio;
|
||||
|
||||
ddata->hpd_gpio = gpio;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->ops.hdmi = &tpd_hdmi_ops;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
|
||||
dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->port_num = 1;
|
||||
|
||||
in = ddata->in;
|
||||
|
||||
r = omapdss_register_output(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register output\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_reg:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit tpd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_output(&ddata->dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_enabled(dssdev));
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
tpd_disable(dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_connected(dssdev));
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
tpd_disconnect(dssdev, dssdev->dst);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tpd_of_match[] = {
|
||||
{ .compatible = "omapdss,ti,tpd12s015", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, tpd_of_match);
|
||||
|
||||
static struct platform_driver tpd_driver = {
|
||||
.probe = tpd_probe,
|
||||
.remove = __exit_p(tpd_remove),
|
||||
.driver = {
|
||||
.name = "tpd12s015",
|
||||
.of_match_table = tpd_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(tpd_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("TPD12S015 driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* Generic MIPI DPI Panel Driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
#include <video/of_display_timing.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
int data_lines;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
/* used for non-DT boot, to be removed */
|
||||
int backlight_gpio;
|
||||
|
||||
struct gpio_desc *enable_gpio;
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int panel_dpi_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void panel_dpi_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int panel_dpi_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio))
|
||||
gpio_set_value_cansleep(ddata->backlight_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void panel_dpi_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio))
|
||||
gpio_set_value_cansleep(ddata->backlight_gpio, 0);
|
||||
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver panel_dpi_ops = {
|
||||
.connect = panel_dpi_connect,
|
||||
.disconnect = panel_dpi_disconnect,
|
||||
|
||||
.enable = panel_dpi_enable,
|
||||
.disable = panel_dpi_disable,
|
||||
|
||||
.set_timings = panel_dpi_set_timings,
|
||||
.get_timings = panel_dpi_get_timings,
|
||||
.check_timings = panel_dpi_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
static int panel_dpi_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
const struct panel_dpi_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
struct videomode vm;
|
||||
int r;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&pdev->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
videomode_from_timing(pdata->display_timing, &vm);
|
||||
videomode_to_omap_video_timings(&vm, &ddata->videomode);
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
r = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "panel enable");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
|
||||
ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio);
|
||||
|
||||
ddata->backlight_gpio = pdata->backlight_gpio;
|
||||
|
||||
return 0;
|
||||
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int panel_dpi_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
int r;
|
||||
struct display_timing timing;
|
||||
struct videomode vm;
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
ddata->enable_gpio = gpio;
|
||||
|
||||
ddata->backlight_gpio = -ENOENT;
|
||||
|
||||
r = of_get_display_timing(node, "panel-timing", &timing);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failed to get video timing\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
videomode_from_timing(&timing, &vm);
|
||||
videomode_to_omap_video_timings(&vm, &ddata->videomode);
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int panel_dpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = panel_dpi_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = panel_dpi_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio)) {
|
||||
r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "panel backlight");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->driver = &panel_dpi_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
dssdev->phy.dpi.data_lines = ddata->data_lines;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit panel_dpi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
panel_dpi_disable(dssdev);
|
||||
panel_dpi_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id panel_dpi_of_match[] = {
|
||||
{ .compatible = "omapdss,panel-dpi", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
|
||||
|
||||
static struct platform_driver panel_dpi_driver = {
|
||||
.probe = panel_dpi_probe,
|
||||
.remove = __exit_p(panel_dpi_remove),
|
||||
.driver = {
|
||||
.name = "panel-dpi",
|
||||
.of_match_table = panel_dpi_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(panel_dpi_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
|
||||
MODULE_LICENSE("GPL");
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
* LG.Philips LB035Q02 LCD Panel driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
* Based on a driver by: Steve Sakoman <steve@sakoman.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/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
static struct omap_video_timings lb035q02_timings = {
|
||||
.x_res = 320,
|
||||
.y_res = 240,
|
||||
|
||||
.pixelclock = 6500000,
|
||||
|
||||
.hsw = 2,
|
||||
.hfp = 20,
|
||||
.hbp = 68,
|
||||
|
||||
.vsw = 2,
|
||||
.vfp = 4,
|
||||
.vbp = 18,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
};
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct spi_device *spi;
|
||||
|
||||
int data_lines;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
/* used for non-DT boot, to be removed */
|
||||
int backlight_gpio;
|
||||
|
||||
struct gpio_desc *enable_gpio;
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct spi_transfer index_xfer = {
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
};
|
||||
struct spi_transfer value_xfer = {
|
||||
.len = 3,
|
||||
};
|
||||
u8 buffer[16];
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
/* register index */
|
||||
buffer[0] = 0x70;
|
||||
buffer[1] = 0x00;
|
||||
buffer[2] = reg & 0x7f;
|
||||
index_xfer.tx_buf = buffer;
|
||||
spi_message_add_tail(&index_xfer, &msg);
|
||||
|
||||
/* register value */
|
||||
buffer[4] = 0x72;
|
||||
buffer[5] = val >> 8;
|
||||
buffer[6] = val;
|
||||
value_xfer.tx_buf = buffer + 4;
|
||||
spi_message_add_tail(&value_xfer, &msg);
|
||||
|
||||
return spi_sync(spi, &msg);
|
||||
}
|
||||
|
||||
static void init_lb035q02_panel(struct spi_device *spi)
|
||||
{
|
||||
/* Init sequence from page 28 of the lb035q02 spec */
|
||||
lb035q02_write_reg(spi, 0x01, 0x6300);
|
||||
lb035q02_write_reg(spi, 0x02, 0x0200);
|
||||
lb035q02_write_reg(spi, 0x03, 0x0177);
|
||||
lb035q02_write_reg(spi, 0x04, 0x04c7);
|
||||
lb035q02_write_reg(spi, 0x05, 0xffc0);
|
||||
lb035q02_write_reg(spi, 0x06, 0xe806);
|
||||
lb035q02_write_reg(spi, 0x0a, 0x4008);
|
||||
lb035q02_write_reg(spi, 0x0b, 0x0000);
|
||||
lb035q02_write_reg(spi, 0x0d, 0x0030);
|
||||
lb035q02_write_reg(spi, 0x0e, 0x2800);
|
||||
lb035q02_write_reg(spi, 0x0f, 0x0000);
|
||||
lb035q02_write_reg(spi, 0x16, 0x9f80);
|
||||
lb035q02_write_reg(spi, 0x17, 0x0a0f);
|
||||
lb035q02_write_reg(spi, 0x1e, 0x00c1);
|
||||
lb035q02_write_reg(spi, 0x30, 0x0300);
|
||||
lb035q02_write_reg(spi, 0x31, 0x0007);
|
||||
lb035q02_write_reg(spi, 0x32, 0x0000);
|
||||
lb035q02_write_reg(spi, 0x33, 0x0000);
|
||||
lb035q02_write_reg(spi, 0x34, 0x0707);
|
||||
lb035q02_write_reg(spi, 0x35, 0x0004);
|
||||
lb035q02_write_reg(spi, 0x36, 0x0302);
|
||||
lb035q02_write_reg(spi, 0x37, 0x0202);
|
||||
lb035q02_write_reg(spi, 0x3a, 0x0a0d);
|
||||
lb035q02_write_reg(spi, 0x3b, 0x0806);
|
||||
}
|
||||
|
||||
static int lb035q02_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
init_lb035q02_panel(ddata->spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lb035q02_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int lb035q02_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (ddata->enable_gpio)
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio))
|
||||
gpio_set_value_cansleep(ddata->backlight_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lb035q02_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (ddata->enable_gpio)
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio))
|
||||
gpio_set_value_cansleep(ddata->backlight_gpio, 0);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void lb035q02_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void lb035q02_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int lb035q02_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver lb035q02_ops = {
|
||||
.connect = lb035q02_connect,
|
||||
.disconnect = lb035q02_disconnect,
|
||||
|
||||
.enable = lb035q02_enable,
|
||||
.disable = lb035q02_disable,
|
||||
|
||||
.set_timings = lb035q02_set_timings,
|
||||
.get_timings = lb035q02_get_timings,
|
||||
.check_timings = lb035q02_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
static int lb035q02_probe_pdata(struct spi_device *spi)
|
||||
{
|
||||
const struct panel_lb035q02_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
int r;
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&spi->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
r = devm_gpio_request_one(&spi->dev, pdata->enable_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "panel enable");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
|
||||
ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio);
|
||||
|
||||
ddata->backlight_gpio = pdata->backlight_gpio;
|
||||
|
||||
return 0;
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int lb035q02_probe_of(struct spi_device *spi)
|
||||
{
|
||||
struct device_node *node = spi->dev.of_node;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *in;
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(&spi->dev, "failed to parse enable gpio\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ddata->enable_gpio = gpio;
|
||||
|
||||
ddata->backlight_gpio = -ENOENT;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&spi->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lb035q02_panel_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ddata);
|
||||
|
||||
ddata->spi = spi;
|
||||
|
||||
if (dev_get_platdata(&spi->dev)) {
|
||||
r = lb035q02_probe_pdata(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (spi->dev.of_node) {
|
||||
r = lb035q02_probe_of(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio)) {
|
||||
r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "panel backlight");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
ddata->videomode = lb035q02_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &spi->dev;
|
||||
dssdev->driver = &lb035q02_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
dssdev->phy.dpi.data_lines = ddata->data_lines;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int lb035q02_panel_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
lb035q02_disable(dssdev);
|
||||
lb035q02_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id lb035q02_of_match[] = {
|
||||
{ .compatible = "omapdss,lgphilips,lb035q02", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, lb035q02_of_match);
|
||||
|
||||
static struct spi_driver lb035q02_spi_driver = {
|
||||
.probe = lb035q02_panel_spi_probe,
|
||||
.remove = lb035q02_panel_spi_remove,
|
||||
.driver = {
|
||||
.name = "panel_lgphilips_lb035q02",
|
||||
.of_match_table = lb035q02_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_spi_driver(lb035q02_spi_driver);
|
||||
|
||||
MODULE_ALIAS("spi:lgphilips,lb035q02");
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
* NEC NL8048HL11 Panel driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments Inc.
|
||||
* Author: Erik Gilling <konkers@android.com>
|
||||
* Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
int data_lines;
|
||||
|
||||
int res_gpio;
|
||||
int qvga_gpio;
|
||||
|
||||
struct spi_device *spi;
|
||||
};
|
||||
|
||||
#define LCD_XRES 800
|
||||
#define LCD_YRES 480
|
||||
/*
|
||||
* NEC PIX Clock Ratings
|
||||
* MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz
|
||||
*/
|
||||
#define LCD_PIXEL_CLOCK 23800000
|
||||
|
||||
static const struct {
|
||||
unsigned char addr;
|
||||
unsigned char dat;
|
||||
} nec_8048_init_seq[] = {
|
||||
{ 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 }, { 5, 0x14 },
|
||||
{ 6, 0x24 }, { 16, 0xD7 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x55 },
|
||||
{ 20, 0x01 }, { 21, 0x70 }, { 22, 0x1E }, { 23, 0x25 }, { 24, 0x25 },
|
||||
{ 25, 0x02 }, { 26, 0x02 }, { 27, 0xA0 }, { 32, 0x2F }, { 33, 0x0F },
|
||||
{ 34, 0x0F }, { 35, 0x0F }, { 36, 0x0F }, { 37, 0x0F }, { 38, 0x0F },
|
||||
{ 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 }, { 42, 0x02 }, { 43, 0x0F },
|
||||
{ 44, 0x0F }, { 45, 0x0F }, { 46, 0x0F }, { 47, 0x0F }, { 48, 0x0F },
|
||||
{ 49, 0x0F }, { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 },
|
||||
{ 80, 0x0C }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 }, { 86, 0x14 },
|
||||
{ 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 }, { 92, 0x02 }, { 93, 0x0C },
|
||||
{ 94, 0x1C }, { 95, 0x27 }, { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 },
|
||||
{ 103, 0x27 }, { 112, 0x01 }, { 113, 0x0E }, { 114, 0x02 },
|
||||
{ 115, 0x0C }, { 118, 0x0C }, { 121, 0x30 }, { 130, 0x00 },
|
||||
{ 131, 0x00 }, { 132, 0xFC }, { 134, 0x00 }, { 136, 0x00 },
|
||||
{ 138, 0x00 }, { 139, 0x00 }, { 140, 0x00 }, { 141, 0xFC },
|
||||
{ 143, 0x00 }, { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 },
|
||||
{ 149, 0x00 }, { 150, 0xFC }, { 152, 0x00 }, { 154, 0x00 },
|
||||
{ 156, 0x00 }, { 157, 0x00 }, { 2, 0x00 },
|
||||
};
|
||||
|
||||
static const struct omap_video_timings nec_8048_panel_timings = {
|
||||
.x_res = LCD_XRES,
|
||||
.y_res = LCD_YRES,
|
||||
.pixelclock = LCD_PIXEL_CLOCK,
|
||||
.hfp = 6,
|
||||
.hsw = 1,
|
||||
.hbp = 4,
|
||||
.vfp = 3,
|
||||
.vsw = 1,
|
||||
.vbp = 4,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int nec_8048_spi_send(struct spi_device *spi, unsigned char reg_addr,
|
||||
unsigned char reg_data)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int cmd = 0, data = 0;
|
||||
|
||||
cmd = 0x0000 | reg_addr; /* register address write */
|
||||
data = 0x0100 | reg_data; /* register data write */
|
||||
data = (cmd << 16) | data;
|
||||
|
||||
ret = spi_write(spi, (unsigned char *)&data, 4);
|
||||
if (ret)
|
||||
pr_err("error in spi_write %x\n", data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_nec_8048_wvga_lcd(struct spi_device *spi)
|
||||
{
|
||||
unsigned int i;
|
||||
/* Initialization Sequence */
|
||||
/* nec_8048_spi_send(spi, REG, VAL) */
|
||||
for (i = 0; i < (ARRAY_SIZE(nec_8048_init_seq) - 1); i++)
|
||||
nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
|
||||
nec_8048_init_seq[i].dat);
|
||||
udelay(20);
|
||||
nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
|
||||
nec_8048_init_seq[i].dat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nec_8048_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nec_8048_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int nec_8048_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (gpio_is_valid(ddata->res_gpio))
|
||||
gpio_set_value_cansleep(ddata->res_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nec_8048_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (gpio_is_valid(ddata->res_gpio))
|
||||
gpio_set_value_cansleep(ddata->res_gpio, 0);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void nec_8048_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void nec_8048_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int nec_8048_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver nec_8048_ops = {
|
||||
.connect = nec_8048_connect,
|
||||
.disconnect = nec_8048_disconnect,
|
||||
|
||||
.enable = nec_8048_enable,
|
||||
.disable = nec_8048_disable,
|
||||
|
||||
.set_timings = nec_8048_set_timings,
|
||||
.get_timings = nec_8048_get_timings,
|
||||
.check_timings = nec_8048_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
|
||||
static int nec_8048_probe_pdata(struct spi_device *spi)
|
||||
{
|
||||
const struct panel_nec_nl8048hl11_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
ddata->qvga_gpio = pdata->qvga_gpio;
|
||||
ddata->res_gpio = pdata->res_gpio;
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&spi->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nec_8048_probe_of(struct spi_device *spi)
|
||||
{
|
||||
struct device_node *node = spi->dev.of_node;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *in;
|
||||
int gpio;
|
||||
|
||||
gpio = of_get_named_gpio(node, "reset-gpios", 0);
|
||||
if (!gpio_is_valid(gpio)) {
|
||||
dev_err(&spi->dev, "failed to parse enable gpio\n");
|
||||
return gpio;
|
||||
}
|
||||
ddata->res_gpio = gpio;
|
||||
|
||||
/* XXX the panel spec doesn't mention any QVGA pin?? */
|
||||
ddata->qvga_gpio = -ENOENT;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&spi->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nec_8048_probe(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
dev_dbg(&spi->dev, "%s\n", __func__);
|
||||
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi->bits_per_word = 32;
|
||||
|
||||
r = spi_setup(spi);
|
||||
if (r < 0) {
|
||||
dev_err(&spi->dev, "spi_setup failed: %d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
init_nec_8048_wvga_lcd(spi);
|
||||
|
||||
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ddata);
|
||||
|
||||
ddata->spi = spi;
|
||||
|
||||
if (dev_get_platdata(&spi->dev)) {
|
||||
r = nec_8048_probe_pdata(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (spi->dev.of_node) {
|
||||
r = nec_8048_probe_of(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->qvga_gpio)) {
|
||||
r = devm_gpio_request_one(&spi->dev, ddata->qvga_gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "lcd QVGA");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->res_gpio)) {
|
||||
r = devm_gpio_request_one(&spi->dev, ddata->res_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "lcd RES");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
ddata->videomode = nec_8048_panel_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &spi->dev;
|
||||
dssdev->driver = &nec_8048_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int nec_8048_remove(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
nec_8048_disable(dssdev);
|
||||
nec_8048_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int nec_8048_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
nec_8048_spi_send(spi, 2, 0x01);
|
||||
mdelay(40);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nec_8048_resume(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
/* reinitialize the panel */
|
||||
spi_setup(spi);
|
||||
nec_8048_spi_send(spi, 2, 0x00);
|
||||
init_nec_8048_wvga_lcd(spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static SIMPLE_DEV_PM_OPS(nec_8048_pm_ops, nec_8048_suspend,
|
||||
nec_8048_resume);
|
||||
#define NEC_8048_PM_OPS (&nec_8048_pm_ops)
|
||||
#else
|
||||
#define NEC_8048_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct of_device_id nec_8048_of_match[] = {
|
||||
{ .compatible = "omapdss,nec,nl8048hl11", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, nec_8048_of_match);
|
||||
|
||||
static struct spi_driver nec_8048_driver = {
|
||||
.driver = {
|
||||
.name = "panel-nec-nl8048hl11",
|
||||
.pm = NEC_8048_PM_OPS,
|
||||
.of_match_table = nec_8048_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = nec_8048_probe,
|
||||
.remove = nec_8048_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(nec_8048_driver);
|
||||
|
||||
MODULE_ALIAS("spi:nec,nl8048hl11");
|
||||
MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
|
||||
MODULE_DESCRIPTION("NEC-NL8048HL11 Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* LCD panel driver for Sharp LS037V7DW01
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
struct regulator *vcc;
|
||||
|
||||
int data_lines;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
struct gpio_desc *resb_gpio; /* low = reset active min 20 us */
|
||||
struct gpio_desc *ini_gpio; /* high = power on */
|
||||
struct gpio_desc *mo_gpio; /* low = 480x640, high = 240x320 */
|
||||
struct gpio_desc *lr_gpio; /* high = conventional horizontal scanning */
|
||||
struct gpio_desc *ud_gpio; /* high = conventional vertical scanning */
|
||||
};
|
||||
|
||||
static const struct omap_video_timings sharp_ls_timings = {
|
||||
.x_res = 480,
|
||||
.y_res = 640,
|
||||
|
||||
.pixelclock = 19200000,
|
||||
|
||||
.hsw = 2,
|
||||
.hfp = 1,
|
||||
.hbp = 28,
|
||||
|
||||
.vsw = 1,
|
||||
.vfp = 1,
|
||||
.vbp = 1,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int sharp_ls_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sharp_ls_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int sharp_ls_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
if (ddata->vcc) {
|
||||
r = regulator_enable(ddata->vcc);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r) {
|
||||
regulator_disable(ddata->vcc);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* wait couple of vsyncs until enabling the LCD */
|
||||
msleep(50);
|
||||
|
||||
if (ddata->resb_gpio)
|
||||
gpiod_set_value_cansleep(ddata->resb_gpio, 1);
|
||||
|
||||
if (ddata->ini_gpio)
|
||||
gpiod_set_value_cansleep(ddata->ini_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sharp_ls_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (ddata->ini_gpio)
|
||||
gpiod_set_value_cansleep(ddata->ini_gpio, 0);
|
||||
|
||||
if (ddata->resb_gpio)
|
||||
gpiod_set_value_cansleep(ddata->resb_gpio, 0);
|
||||
|
||||
/* wait at least 5 vsyncs after disabling the LCD */
|
||||
|
||||
msleep(100);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
if (ddata->vcc)
|
||||
regulator_disable(ddata->vcc);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void sharp_ls_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void sharp_ls_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int sharp_ls_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver sharp_ls_ops = {
|
||||
.connect = sharp_ls_connect,
|
||||
.disconnect = sharp_ls_disconnect,
|
||||
|
||||
.enable = sharp_ls_enable,
|
||||
.disable = sharp_ls_disable,
|
||||
|
||||
.set_timings = sharp_ls_set_timings,
|
||||
.get_timings = sharp_ls_get_timings,
|
||||
.check_timings = sharp_ls_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
static int sharp_ls_get_gpio(struct device *dev, int gpio, unsigned long flags,
|
||||
char *desc, struct gpio_desc **gpiod)
|
||||
{
|
||||
struct gpio_desc *gd;
|
||||
int r;
|
||||
|
||||
*gpiod = NULL;
|
||||
|
||||
r = devm_gpio_request_one(dev, gpio, flags, desc);
|
||||
if (r)
|
||||
return r == -ENOENT ? 0 : r;
|
||||
|
||||
gd = gpio_to_desc(gpio);
|
||||
if (IS_ERR(gd))
|
||||
return PTR_ERR(gd) == -ENOENT ? 0 : PTR_ERR(gd);
|
||||
|
||||
*gpiod = gd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sharp_ls_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
const struct panel_sharp_ls037v7dw01_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
int r;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&pdev->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
r = sharp_ls_get_gpio(&pdev->dev, pdata->mo_gpio, GPIOF_OUT_INIT_LOW,
|
||||
"lcd MO", &ddata->mo_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
r = sharp_ls_get_gpio(&pdev->dev, pdata->lr_gpio, GPIOF_OUT_INIT_HIGH,
|
||||
"lcd LR", &ddata->lr_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
r = sharp_ls_get_gpio(&pdev->dev, pdata->ud_gpio, GPIOF_OUT_INIT_HIGH,
|
||||
"lcd UD", &ddata->ud_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
r = sharp_ls_get_gpio(&pdev->dev, pdata->resb_gpio, GPIOF_OUT_INIT_LOW,
|
||||
"lcd RESB", &ddata->resb_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
r = sharp_ls_get_gpio(&pdev->dev, pdata->ini_gpio, GPIOF_OUT_INIT_LOW,
|
||||
"lcd INI", &ddata->ini_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
|
||||
const char *desc, struct gpio_desc **gpiod)
|
||||
{
|
||||
struct gpio_desc *gd;
|
||||
|
||||
*gpiod = NULL;
|
||||
|
||||
gd = devm_gpiod_get_index(dev, desc, index, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gd))
|
||||
return PTR_ERR(gd);
|
||||
|
||||
*gpiod = gd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sharp_ls_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
int r;
|
||||
|
||||
ddata->vcc = devm_regulator_get(&pdev->dev, "envdd");
|
||||
if (IS_ERR(ddata->vcc)) {
|
||||
dev_err(&pdev->dev, "failed to get regulator\n");
|
||||
return PTR_ERR(ddata->vcc);
|
||||
}
|
||||
|
||||
/* lcd INI */
|
||||
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "enable", &ddata->ini_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* lcd RESB */
|
||||
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "reset", &ddata->resb_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* lcd MO */
|
||||
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "mode", &ddata->mo_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* lcd LR */
|
||||
r = sharp_ls_get_gpio_of(&pdev->dev, 1, 1, "mode", &ddata->lr_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* lcd UD */
|
||||
r = sharp_ls_get_gpio_of(&pdev->dev, 2, 1, "mode", &ddata->ud_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sharp_ls_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = sharp_ls_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = sharp_ls_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->videomode = sharp_ls_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->driver = &sharp_ls_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
dssdev->phy.dpi.data_lines = ddata->data_lines;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit sharp_ls_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
sharp_ls_disable(dssdev);
|
||||
sharp_ls_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sharp_ls_of_match[] = {
|
||||
{ .compatible = "omapdss,sharp,ls037v7dw01", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, sharp_ls_of_match);
|
||||
|
||||
static struct platform_driver sharp_ls_driver = {
|
||||
.probe = sharp_ls_probe,
|
||||
.remove = __exit_p(sharp_ls_remove),
|
||||
.driver = {
|
||||
.name = "panel-sharp-ls037v7dw01",
|
||||
.of_match_table = sharp_ls_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(sharp_ls_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,917 @@
|
|||
/*
|
||||
* Sony ACX565AKM LCD Panel driver
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation
|
||||
*
|
||||
* Original Driver Author: Imre Deak <imre.deak@nokia.com>
|
||||
* Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
||||
* Adapted to new DSS2 framework: Roger Quadros <roger.quadros@nokia.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
#define MIPID_CMD_READ_DISP_ID 0x04
|
||||
#define MIPID_CMD_READ_RED 0x06
|
||||
#define MIPID_CMD_READ_GREEN 0x07
|
||||
#define MIPID_CMD_READ_BLUE 0x08
|
||||
#define MIPID_CMD_READ_DISP_STATUS 0x09
|
||||
#define MIPID_CMD_RDDSDR 0x0F
|
||||
#define MIPID_CMD_SLEEP_IN 0x10
|
||||
#define MIPID_CMD_SLEEP_OUT 0x11
|
||||
#define MIPID_CMD_DISP_OFF 0x28
|
||||
#define MIPID_CMD_DISP_ON 0x29
|
||||
#define MIPID_CMD_WRITE_DISP_BRIGHTNESS 0x51
|
||||
#define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52
|
||||
#define MIPID_CMD_WRITE_CTRL_DISP 0x53
|
||||
|
||||
#define CTRL_DISP_BRIGHTNESS_CTRL_ON (1 << 5)
|
||||
#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON (1 << 4)
|
||||
#define CTRL_DISP_BACKLIGHT_ON (1 << 2)
|
||||
#define CTRL_DISP_AUTO_BRIGHTNESS_ON (1 << 1)
|
||||
|
||||
#define MIPID_CMD_READ_CTRL_DISP 0x54
|
||||
#define MIPID_CMD_WRITE_CABC 0x55
|
||||
#define MIPID_CMD_READ_CABC 0x56
|
||||
|
||||
#define MIPID_VER_LPH8923 3
|
||||
#define MIPID_VER_LS041Y3 4
|
||||
#define MIPID_VER_L4F00311 8
|
||||
#define MIPID_VER_ACX565AKM 9
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
int reset_gpio;
|
||||
int datapairs;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
char *name;
|
||||
int enabled;
|
||||
int model;
|
||||
int revision;
|
||||
u8 display_id[3];
|
||||
unsigned has_bc:1;
|
||||
unsigned has_cabc:1;
|
||||
unsigned cabc_mode;
|
||||
unsigned long hw_guard_end; /* next value of jiffies
|
||||
when we can issue the
|
||||
next sleep in/out command */
|
||||
unsigned long hw_guard_wait; /* max guard time in jiffies */
|
||||
|
||||
struct spi_device *spi;
|
||||
struct mutex mutex;
|
||||
|
||||
struct backlight_device *bl_dev;
|
||||
};
|
||||
|
||||
static const struct omap_video_timings acx565akm_panel_timings = {
|
||||
.x_res = 800,
|
||||
.y_res = 480,
|
||||
.pixelclock = 24000000,
|
||||
.hfp = 28,
|
||||
.hsw = 4,
|
||||
.hbp = 24,
|
||||
.vfp = 3,
|
||||
.vsw = 3,
|
||||
.vbp = 4,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static void acx565akm_transfer(struct panel_drv_data *ddata, int cmd,
|
||||
const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
|
||||
{
|
||||
struct spi_message m;
|
||||
struct spi_transfer *x, xfer[5];
|
||||
int r;
|
||||
|
||||
BUG_ON(ddata->spi == NULL);
|
||||
|
||||
spi_message_init(&m);
|
||||
|
||||
memset(xfer, 0, sizeof(xfer));
|
||||
x = &xfer[0];
|
||||
|
||||
cmd &= 0xff;
|
||||
x->tx_buf = &cmd;
|
||||
x->bits_per_word = 9;
|
||||
x->len = 2;
|
||||
|
||||
if (rlen > 1 && wlen == 0) {
|
||||
/*
|
||||
* Between the command and the response data there is a
|
||||
* dummy clock cycle. Add an extra bit after the command
|
||||
* word to account for this.
|
||||
*/
|
||||
x->bits_per_word = 10;
|
||||
cmd <<= 1;
|
||||
}
|
||||
spi_message_add_tail(x, &m);
|
||||
|
||||
if (wlen) {
|
||||
x++;
|
||||
x->tx_buf = wbuf;
|
||||
x->len = wlen;
|
||||
x->bits_per_word = 9;
|
||||
spi_message_add_tail(x, &m);
|
||||
}
|
||||
|
||||
if (rlen) {
|
||||
x++;
|
||||
x->rx_buf = rbuf;
|
||||
x->len = rlen;
|
||||
spi_message_add_tail(x, &m);
|
||||
}
|
||||
|
||||
r = spi_sync(ddata->spi, &m);
|
||||
if (r < 0)
|
||||
dev_dbg(&ddata->spi->dev, "spi_sync %d\n", r);
|
||||
}
|
||||
|
||||
static inline void acx565akm_cmd(struct panel_drv_data *ddata, int cmd)
|
||||
{
|
||||
acx565akm_transfer(ddata, cmd, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static inline void acx565akm_write(struct panel_drv_data *ddata,
|
||||
int reg, const u8 *buf, int len)
|
||||
{
|
||||
acx565akm_transfer(ddata, reg, buf, len, NULL, 0);
|
||||
}
|
||||
|
||||
static inline void acx565akm_read(struct panel_drv_data *ddata,
|
||||
int reg, u8 *buf, int len)
|
||||
{
|
||||
acx565akm_transfer(ddata, reg, NULL, 0, buf, len);
|
||||
}
|
||||
|
||||
static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
|
||||
{
|
||||
ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
|
||||
ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
|
||||
}
|
||||
|
||||
static void hw_guard_wait(struct panel_drv_data *ddata)
|
||||
{
|
||||
unsigned long wait = ddata->hw_guard_end - jiffies;
|
||||
|
||||
if ((long)wait > 0 && wait <= ddata->hw_guard_wait) {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(wait);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_sleep_mode(struct panel_drv_data *ddata, int on)
|
||||
{
|
||||
int cmd;
|
||||
|
||||
if (on)
|
||||
cmd = MIPID_CMD_SLEEP_IN;
|
||||
else
|
||||
cmd = MIPID_CMD_SLEEP_OUT;
|
||||
/*
|
||||
* We have to keep 120msec between sleep in/out commands.
|
||||
* (8.2.15, 8.2.16).
|
||||
*/
|
||||
hw_guard_wait(ddata);
|
||||
acx565akm_cmd(ddata, cmd);
|
||||
hw_guard_start(ddata, 120);
|
||||
}
|
||||
|
||||
static void set_display_state(struct panel_drv_data *ddata, int enabled)
|
||||
{
|
||||
int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF;
|
||||
|
||||
acx565akm_cmd(ddata, cmd);
|
||||
}
|
||||
|
||||
static int panel_enabled(struct panel_drv_data *ddata)
|
||||
{
|
||||
u32 disp_status;
|
||||
int enabled;
|
||||
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_DISP_STATUS,
|
||||
(u8 *)&disp_status, 4);
|
||||
disp_status = __be32_to_cpu(disp_status);
|
||||
enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10));
|
||||
dev_dbg(&ddata->spi->dev,
|
||||
"LCD panel %senabled by bootloader (status 0x%04x)\n",
|
||||
enabled ? "" : "not ", disp_status);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static int panel_detect(struct panel_drv_data *ddata)
|
||||
{
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_DISP_ID, ddata->display_id, 3);
|
||||
dev_dbg(&ddata->spi->dev, "MIPI display ID: %02x%02x%02x\n",
|
||||
ddata->display_id[0],
|
||||
ddata->display_id[1],
|
||||
ddata->display_id[2]);
|
||||
|
||||
switch (ddata->display_id[0]) {
|
||||
case 0x10:
|
||||
ddata->model = MIPID_VER_ACX565AKM;
|
||||
ddata->name = "acx565akm";
|
||||
ddata->has_bc = 1;
|
||||
ddata->has_cabc = 1;
|
||||
break;
|
||||
case 0x29:
|
||||
ddata->model = MIPID_VER_L4F00311;
|
||||
ddata->name = "l4f00311";
|
||||
break;
|
||||
case 0x45:
|
||||
ddata->model = MIPID_VER_LPH8923;
|
||||
ddata->name = "lph8923";
|
||||
break;
|
||||
case 0x83:
|
||||
ddata->model = MIPID_VER_LS041Y3;
|
||||
ddata->name = "ls041y3";
|
||||
break;
|
||||
default:
|
||||
ddata->name = "unknown";
|
||||
dev_err(&ddata->spi->dev, "invalid display ID\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->revision = ddata->display_id[1];
|
||||
|
||||
dev_info(&ddata->spi->dev, "omapfb: %s rev %02x LCD detected\n",
|
||||
ddata->name, ddata->revision);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------Backlight Control-------------------------*/
|
||||
|
||||
static void enable_backlight_ctrl(struct panel_drv_data *ddata, int enable)
|
||||
{
|
||||
u16 ctrl;
|
||||
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1);
|
||||
if (enable) {
|
||||
ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON |
|
||||
CTRL_DISP_BACKLIGHT_ON;
|
||||
} else {
|
||||
ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON |
|
||||
CTRL_DISP_BACKLIGHT_ON);
|
||||
}
|
||||
|
||||
ctrl |= 1 << 8;
|
||||
acx565akm_write(ddata, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2);
|
||||
}
|
||||
|
||||
static void set_cabc_mode(struct panel_drv_data *ddata, unsigned mode)
|
||||
{
|
||||
u16 cabc_ctrl;
|
||||
|
||||
ddata->cabc_mode = mode;
|
||||
if (!ddata->enabled)
|
||||
return;
|
||||
cabc_ctrl = 0;
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1);
|
||||
cabc_ctrl &= ~3;
|
||||
cabc_ctrl |= (1 << 8) | (mode & 3);
|
||||
acx565akm_write(ddata, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2);
|
||||
}
|
||||
|
||||
static unsigned get_cabc_mode(struct panel_drv_data *ddata)
|
||||
{
|
||||
return ddata->cabc_mode;
|
||||
}
|
||||
|
||||
static unsigned get_hw_cabc_mode(struct panel_drv_data *ddata)
|
||||
{
|
||||
u8 cabc_ctrl;
|
||||
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_CABC, &cabc_ctrl, 1);
|
||||
return cabc_ctrl & 3;
|
||||
}
|
||||
|
||||
static void acx565akm_set_brightness(struct panel_drv_data *ddata, int level)
|
||||
{
|
||||
int bv;
|
||||
|
||||
bv = level | (1 << 8);
|
||||
acx565akm_write(ddata, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2);
|
||||
|
||||
if (level)
|
||||
enable_backlight_ctrl(ddata, 1);
|
||||
else
|
||||
enable_backlight_ctrl(ddata, 0);
|
||||
}
|
||||
|
||||
static int acx565akm_get_actual_brightness(struct panel_drv_data *ddata)
|
||||
{
|
||||
u8 bv;
|
||||
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1);
|
||||
|
||||
return bv;
|
||||
}
|
||||
|
||||
|
||||
static int acx565akm_bl_update_status(struct backlight_device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
|
||||
int level;
|
||||
|
||||
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
|
||||
|
||||
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
|
||||
dev->props.power == FB_BLANK_UNBLANK)
|
||||
level = dev->props.brightness;
|
||||
else
|
||||
level = 0;
|
||||
|
||||
if (ddata->has_bc)
|
||||
acx565akm_set_brightness(ddata, level);
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acx565akm_bl_get_intensity(struct backlight_device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
|
||||
|
||||
dev_dbg(&dev->dev, "%s\n", __func__);
|
||||
|
||||
if (!ddata->has_bc)
|
||||
return -ENODEV;
|
||||
|
||||
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
|
||||
dev->props.power == FB_BLANK_UNBLANK) {
|
||||
if (ddata->has_bc)
|
||||
return acx565akm_get_actual_brightness(ddata);
|
||||
else
|
||||
return dev->props.brightness;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acx565akm_bl_update_status_locked(struct backlight_device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
|
||||
int r;
|
||||
|
||||
mutex_lock(&ddata->mutex);
|
||||
r = acx565akm_bl_update_status(dev);
|
||||
mutex_unlock(&ddata->mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int acx565akm_bl_get_intensity_locked(struct backlight_device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
|
||||
int r;
|
||||
|
||||
mutex_lock(&ddata->mutex);
|
||||
r = acx565akm_bl_get_intensity(dev);
|
||||
mutex_unlock(&ddata->mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct backlight_ops acx565akm_bl_ops = {
|
||||
.get_brightness = acx565akm_bl_get_intensity_locked,
|
||||
.update_status = acx565akm_bl_update_status_locked,
|
||||
};
|
||||
|
||||
/*--------------------Auto Brightness control via Sysfs---------------------*/
|
||||
|
||||
static const char * const cabc_modes[] = {
|
||||
"off", /* always used when CABC is not supported */
|
||||
"ui",
|
||||
"still-image",
|
||||
"moving-image",
|
||||
};
|
||||
|
||||
static ssize_t show_cabc_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
const char *mode_str;
|
||||
int mode;
|
||||
int len;
|
||||
|
||||
if (!ddata->has_cabc)
|
||||
mode = 0;
|
||||
else
|
||||
mode = get_cabc_mode(ddata);
|
||||
mode_str = "unknown";
|
||||
if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
|
||||
mode_str = cabc_modes[mode];
|
||||
len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
|
||||
|
||||
return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
|
||||
}
|
||||
|
||||
static ssize_t store_cabc_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
|
||||
const char *mode_str = cabc_modes[i];
|
||||
int cmp_len = strlen(mode_str);
|
||||
|
||||
if (count > 0 && buf[count - 1] == '\n')
|
||||
count--;
|
||||
if (count != cmp_len)
|
||||
continue;
|
||||
|
||||
if (strncmp(buf, mode_str, cmp_len) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(cabc_modes))
|
||||
return -EINVAL;
|
||||
|
||||
if (!ddata->has_cabc && i != 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ddata->mutex);
|
||||
set_cabc_mode(ddata, i);
|
||||
mutex_unlock(&ddata->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_cabc_available_modes(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
int len;
|
||||
int i;
|
||||
|
||||
if (!ddata->has_cabc)
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]);
|
||||
|
||||
for (i = 0, len = 0;
|
||||
len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
|
||||
len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
|
||||
i ? " " : "", cabc_modes[i],
|
||||
i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
|
||||
|
||||
return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
|
||||
show_cabc_mode, store_cabc_mode);
|
||||
static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
|
||||
show_cabc_available_modes, NULL);
|
||||
|
||||
static struct attribute *bldev_attrs[] = {
|
||||
&dev_attr_cabc_mode.attr,
|
||||
&dev_attr_cabc_available_modes.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group bldev_attr_group = {
|
||||
.attrs = bldev_attrs,
|
||||
};
|
||||
|
||||
static int acx565akm_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.sdi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acx565akm_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.sdi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int acx565akm_panel_power_on(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
|
||||
|
||||
in->ops.sdi->set_timings(in, &ddata->videomode);
|
||||
|
||||
if (ddata->datapairs > 0)
|
||||
in->ops.sdi->set_datapairs(in, ddata->datapairs);
|
||||
|
||||
r = in->ops.sdi->enable(in);
|
||||
if (r) {
|
||||
pr_err("%s sdi enable failed\n", __func__);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*FIXME tweak me */
|
||||
msleep(50);
|
||||
|
||||
if (gpio_is_valid(ddata->reset_gpio))
|
||||
gpio_set_value(ddata->reset_gpio, 1);
|
||||
|
||||
if (ddata->enabled) {
|
||||
dev_dbg(&ddata->spi->dev, "panel already enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to meet all the following delay requirements:
|
||||
* 1. tRW: reset pulse width 10usec (7.12.1)
|
||||
* 2. tRT: reset cancel time 5msec (7.12.1)
|
||||
* 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst
|
||||
* case (7.6.2)
|
||||
* 4. 120msec before the sleep out command (7.12.1)
|
||||
*/
|
||||
msleep(120);
|
||||
|
||||
set_sleep_mode(ddata, 0);
|
||||
ddata->enabled = 1;
|
||||
|
||||
/* 5msec between sleep out and the next command. (8.2.16) */
|
||||
usleep_range(5000, 10000);
|
||||
set_display_state(ddata, 1);
|
||||
set_cabc_mode(ddata, ddata->cabc_mode);
|
||||
|
||||
return acx565akm_bl_update_status(ddata->bl_dev);
|
||||
}
|
||||
|
||||
static void acx565akm_panel_power_off(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(dssdev->dev, "%s\n", __func__);
|
||||
|
||||
if (!ddata->enabled)
|
||||
return;
|
||||
|
||||
set_display_state(ddata, 0);
|
||||
set_sleep_mode(ddata, 1);
|
||||
ddata->enabled = 0;
|
||||
/*
|
||||
* We have to provide PCLK,HS,VS signals for 2 frames (worst case
|
||||
* ~50msec) after sending the sleep in command and asserting the
|
||||
* reset signal. We probably could assert the reset w/o the delay
|
||||
* but we still delay to avoid possible artifacts. (7.6.1)
|
||||
*/
|
||||
msleep(50);
|
||||
|
||||
if (gpio_is_valid(ddata->reset_gpio))
|
||||
gpio_set_value(ddata->reset_gpio, 0);
|
||||
|
||||
/* FIXME need to tweak this delay */
|
||||
msleep(100);
|
||||
|
||||
in->ops.sdi->disable(in);
|
||||
}
|
||||
|
||||
static int acx565akm_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
int r;
|
||||
|
||||
dev_dbg(dssdev->dev, "%s\n", __func__);
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&ddata->mutex);
|
||||
r = acx565akm_panel_power_on(dssdev);
|
||||
mutex_unlock(&ddata->mutex);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acx565akm_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
dev_dbg(dssdev->dev, "%s\n", __func__);
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
mutex_lock(&ddata->mutex);
|
||||
acx565akm_panel_power_off(dssdev);
|
||||
mutex_unlock(&ddata->mutex);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void acx565akm_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.sdi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void acx565akm_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int acx565akm_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.sdi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver acx565akm_ops = {
|
||||
.connect = acx565akm_connect,
|
||||
.disconnect = acx565akm_disconnect,
|
||||
|
||||
.enable = acx565akm_enable,
|
||||
.disable = acx565akm_disable,
|
||||
|
||||
.set_timings = acx565akm_set_timings,
|
||||
.get_timings = acx565akm_get_timings,
|
||||
.check_timings = acx565akm_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
static int acx565akm_probe_pdata(struct spi_device *spi)
|
||||
{
|
||||
const struct panel_acx565akm_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
ddata->reset_gpio = pdata->reset_gpio;
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&spi->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
ddata->in = in;
|
||||
|
||||
ddata->datapairs = pdata->datapairs;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acx565akm_probe_of(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct device_node *np = spi->dev.of_node;
|
||||
|
||||
ddata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
|
||||
|
||||
ddata->in = omapdss_of_find_source_for_first_ep(np);
|
||||
if (IS_ERR(ddata->in)) {
|
||||
dev_err(&spi->dev, "failed to find video source\n");
|
||||
return PTR_ERR(ddata->in);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acx565akm_probe(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
struct backlight_device *bldev;
|
||||
int max_brightness, brightness;
|
||||
struct backlight_properties props;
|
||||
int r;
|
||||
|
||||
dev_dbg(&spi->dev, "%s\n", __func__);
|
||||
|
||||
spi->mode = SPI_MODE_3;
|
||||
|
||||
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ddata);
|
||||
|
||||
ddata->spi = spi;
|
||||
|
||||
mutex_init(&ddata->mutex);
|
||||
|
||||
if (dev_get_platdata(&spi->dev)) {
|
||||
r = acx565akm_probe_pdata(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (spi->dev.of_node) {
|
||||
r = acx565akm_probe_of(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
dev_err(&spi->dev, "platform data missing!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->reset_gpio)) {
|
||||
r = devm_gpio_request_one(&spi->dev, ddata->reset_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "lcd reset");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->reset_gpio))
|
||||
gpio_set_value(ddata->reset_gpio, 1);
|
||||
|
||||
/*
|
||||
* After reset we have to wait 5 msec before the first
|
||||
* command can be sent.
|
||||
*/
|
||||
usleep_range(5000, 10000);
|
||||
|
||||
ddata->enabled = panel_enabled(ddata);
|
||||
|
||||
r = panel_detect(ddata);
|
||||
|
||||
if (!ddata->enabled && gpio_is_valid(ddata->reset_gpio))
|
||||
gpio_set_value(ddata->reset_gpio, 0);
|
||||
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "%s panel detect error\n", __func__);
|
||||
goto err_detect;
|
||||
}
|
||||
|
||||
memset(&props, 0, sizeof(props));
|
||||
props.fb_blank = FB_BLANK_UNBLANK;
|
||||
props.power = FB_BLANK_UNBLANK;
|
||||
props.type = BACKLIGHT_RAW;
|
||||
|
||||
bldev = backlight_device_register("acx565akm", &ddata->spi->dev,
|
||||
ddata, &acx565akm_bl_ops, &props);
|
||||
if (IS_ERR(bldev)) {
|
||||
r = PTR_ERR(bldev);
|
||||
goto err_reg_bl;
|
||||
}
|
||||
ddata->bl_dev = bldev;
|
||||
if (ddata->has_cabc) {
|
||||
r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group);
|
||||
if (r) {
|
||||
dev_err(&bldev->dev,
|
||||
"%s failed to create sysfs files\n", __func__);
|
||||
goto err_sysfs;
|
||||
}
|
||||
ddata->cabc_mode = get_hw_cabc_mode(ddata);
|
||||
}
|
||||
|
||||
max_brightness = 255;
|
||||
|
||||
if (ddata->has_bc)
|
||||
brightness = acx565akm_get_actual_brightness(ddata);
|
||||
else
|
||||
brightness = 0;
|
||||
|
||||
bldev->props.max_brightness = max_brightness;
|
||||
bldev->props.brightness = brightness;
|
||||
|
||||
acx565akm_bl_update_status(bldev);
|
||||
|
||||
|
||||
ddata->videomode = acx565akm_panel_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &spi->dev;
|
||||
dssdev->driver = &acx565akm_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_SDI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
sysfs_remove_group(&bldev->dev.kobj, &bldev_attr_group);
|
||||
err_sysfs:
|
||||
backlight_device_unregister(bldev);
|
||||
err_reg_bl:
|
||||
err_detect:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int acx565akm_remove(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
|
||||
|
||||
sysfs_remove_group(&ddata->bl_dev->dev.kobj, &bldev_attr_group);
|
||||
backlight_device_unregister(ddata->bl_dev);
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
acx565akm_disable(dssdev);
|
||||
acx565akm_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id acx565akm_of_match[] = {
|
||||
{ .compatible = "omapdss,sony,acx565akm", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, acx565akm_of_match);
|
||||
|
||||
static struct spi_driver acx565akm_driver = {
|
||||
.driver = {
|
||||
.name = "acx565akm",
|
||||
.of_match_table = acx565akm_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = acx565akm_probe,
|
||||
.remove = acx565akm_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(acx565akm_driver);
|
||||
|
||||
MODULE_AUTHOR("Nokia Corporation");
|
||||
MODULE_DESCRIPTION("acx565akm LCD Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,511 @@
|
|||
/*
|
||||
* Toppoly TD028TTEC1 panel support
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
||||
*
|
||||
* Neo 1973 code (jbt6k74.c):
|
||||
* Copyright (C) 2006-2007 by OpenMoko, Inc.
|
||||
* Author: Harald Welte <laforge@openmoko.org>
|
||||
*
|
||||
* Ported and adapted from Neo 1973 U-Boot by:
|
||||
* H. Nikolaus Schaller <hns@goldelico.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
int data_lines;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
struct spi_device *spi_dev;
|
||||
};
|
||||
|
||||
static struct omap_video_timings td028ttec1_panel_timings = {
|
||||
.x_res = 480,
|
||||
.y_res = 640,
|
||||
.pixelclock = 22153000,
|
||||
.hfp = 24,
|
||||
.hsw = 8,
|
||||
.hbp = 8,
|
||||
.vfp = 4,
|
||||
.vsw = 2,
|
||||
.vbp = 2,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
};
|
||||
|
||||
#define JBT_COMMAND 0x000
|
||||
#define JBT_DATA 0x100
|
||||
|
||||
static int jbt_ret_write_0(struct panel_drv_data *ddata, u8 reg)
|
||||
{
|
||||
int rc;
|
||||
u16 tx_buf = JBT_COMMAND | reg;
|
||||
|
||||
rc = spi_write(ddata->spi_dev, (u8 *)&tx_buf,
|
||||
1*sizeof(u16));
|
||||
if (rc != 0)
|
||||
dev_err(&ddata->spi_dev->dev,
|
||||
"jbt_ret_write_0 spi_write ret %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jbt_reg_write_1(struct panel_drv_data *ddata, u8 reg, u8 data)
|
||||
{
|
||||
int rc;
|
||||
u16 tx_buf[2];
|
||||
|
||||
tx_buf[0] = JBT_COMMAND | reg;
|
||||
tx_buf[1] = JBT_DATA | data;
|
||||
rc = spi_write(ddata->spi_dev, (u8 *)tx_buf,
|
||||
2*sizeof(u16));
|
||||
if (rc != 0)
|
||||
dev_err(&ddata->spi_dev->dev,
|
||||
"jbt_reg_write_1 spi_write ret %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jbt_reg_write_2(struct panel_drv_data *ddata, u8 reg, u16 data)
|
||||
{
|
||||
int rc;
|
||||
u16 tx_buf[3];
|
||||
|
||||
tx_buf[0] = JBT_COMMAND | reg;
|
||||
tx_buf[1] = JBT_DATA | (data >> 8);
|
||||
tx_buf[2] = JBT_DATA | (data & 0xff);
|
||||
|
||||
rc = spi_write(ddata->spi_dev, (u8 *)tx_buf,
|
||||
3*sizeof(u16));
|
||||
|
||||
if (rc != 0)
|
||||
dev_err(&ddata->spi_dev->dev,
|
||||
"jbt_reg_write_2 spi_write ret %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
enum jbt_register {
|
||||
JBT_REG_SLEEP_IN = 0x10,
|
||||
JBT_REG_SLEEP_OUT = 0x11,
|
||||
|
||||
JBT_REG_DISPLAY_OFF = 0x28,
|
||||
JBT_REG_DISPLAY_ON = 0x29,
|
||||
|
||||
JBT_REG_RGB_FORMAT = 0x3a,
|
||||
JBT_REG_QUAD_RATE = 0x3b,
|
||||
|
||||
JBT_REG_POWER_ON_OFF = 0xb0,
|
||||
JBT_REG_BOOSTER_OP = 0xb1,
|
||||
JBT_REG_BOOSTER_MODE = 0xb2,
|
||||
JBT_REG_BOOSTER_FREQ = 0xb3,
|
||||
JBT_REG_OPAMP_SYSCLK = 0xb4,
|
||||
JBT_REG_VSC_VOLTAGE = 0xb5,
|
||||
JBT_REG_VCOM_VOLTAGE = 0xb6,
|
||||
JBT_REG_EXT_DISPL = 0xb7,
|
||||
JBT_REG_OUTPUT_CONTROL = 0xb8,
|
||||
JBT_REG_DCCLK_DCEV = 0xb9,
|
||||
JBT_REG_DISPLAY_MODE1 = 0xba,
|
||||
JBT_REG_DISPLAY_MODE2 = 0xbb,
|
||||
JBT_REG_DISPLAY_MODE = 0xbc,
|
||||
JBT_REG_ASW_SLEW = 0xbd,
|
||||
JBT_REG_DUMMY_DISPLAY = 0xbe,
|
||||
JBT_REG_DRIVE_SYSTEM = 0xbf,
|
||||
|
||||
JBT_REG_SLEEP_OUT_FR_A = 0xc0,
|
||||
JBT_REG_SLEEP_OUT_FR_B = 0xc1,
|
||||
JBT_REG_SLEEP_OUT_FR_C = 0xc2,
|
||||
JBT_REG_SLEEP_IN_LCCNT_D = 0xc3,
|
||||
JBT_REG_SLEEP_IN_LCCNT_E = 0xc4,
|
||||
JBT_REG_SLEEP_IN_LCCNT_F = 0xc5,
|
||||
JBT_REG_SLEEP_IN_LCCNT_G = 0xc6,
|
||||
|
||||
JBT_REG_GAMMA1_FINE_1 = 0xc7,
|
||||
JBT_REG_GAMMA1_FINE_2 = 0xc8,
|
||||
JBT_REG_GAMMA1_INCLINATION = 0xc9,
|
||||
JBT_REG_GAMMA1_BLUE_OFFSET = 0xca,
|
||||
|
||||
JBT_REG_BLANK_CONTROL = 0xcf,
|
||||
JBT_REG_BLANK_TH_TV = 0xd0,
|
||||
JBT_REG_CKV_ON_OFF = 0xd1,
|
||||
JBT_REG_CKV_1_2 = 0xd2,
|
||||
JBT_REG_OEV_TIMING = 0xd3,
|
||||
JBT_REG_ASW_TIMING_1 = 0xd4,
|
||||
JBT_REG_ASW_TIMING_2 = 0xd5,
|
||||
|
||||
JBT_REG_HCLOCK_VGA = 0xec,
|
||||
JBT_REG_HCLOCK_QVGA = 0xed,
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int td028ttec1_panel_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void td028ttec1_panel_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n",
|
||||
dssdev->state);
|
||||
|
||||
/* three times command zero */
|
||||
r |= jbt_ret_write_0(ddata, 0x00);
|
||||
usleep_range(1000, 2000);
|
||||
r |= jbt_ret_write_0(ddata, 0x00);
|
||||
usleep_range(1000, 2000);
|
||||
r |= jbt_ret_write_0(ddata, 0x00);
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
if (r) {
|
||||
dev_warn(dssdev->dev, "transfer error\n");
|
||||
goto transfer_err;
|
||||
}
|
||||
|
||||
/* deep standby out */
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x17);
|
||||
|
||||
/* RGB I/F on, RAM write off, QVGA through, SIGCON enable */
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE, 0x80);
|
||||
|
||||
/* Quad mode off */
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_QUAD_RATE, 0x00);
|
||||
|
||||
/* AVDD on, XVDD on */
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x16);
|
||||
|
||||
/* Output control */
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0xfff9);
|
||||
|
||||
/* Sleep mode off */
|
||||
r |= jbt_ret_write_0(ddata, JBT_REG_SLEEP_OUT);
|
||||
|
||||
/* at this point we have like 50% grey */
|
||||
|
||||
/* initialize register set */
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE1, 0x01);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE2, 0x00);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_RGB_FORMAT, 0x60);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DRIVE_SYSTEM, 0x10);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_OP, 0x56);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_MODE, 0x33);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_OPAMP_SYSCLK, 0x02);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_VSC_VOLTAGE, 0x2b);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_VCOM_VOLTAGE, 0x40);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_EXT_DISPL, 0x03);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DCCLK_DCEV, 0x04);
|
||||
/*
|
||||
* default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement
|
||||
* to avoid red / blue flicker
|
||||
*/
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_ASW_SLEW, 0x04);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DUMMY_DISPLAY, 0x00);
|
||||
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_A, 0x11);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_B, 0x11);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_C, 0x11);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0);
|
||||
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_GAMMA1_FINE_1, 0x5533);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_FINE_2, 0x00);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_INCLINATION, 0x00);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
|
||||
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_HCLOCK_VGA, 0x1f0);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_BLANK_CONTROL, 0x02);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_BLANK_TH_TV, 0x0804);
|
||||
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_CKV_ON_OFF, 0x01);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_CKV_1_2, 0x0000);
|
||||
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_OEV_TIMING, 0x0d0e);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_ASW_TIMING_1, 0x11a4);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_ASW_TIMING_2, 0x0e);
|
||||
|
||||
r |= jbt_ret_write_0(ddata, JBT_REG_DISPLAY_ON);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
transfer_err:
|
||||
|
||||
return r ? -EIO : 0;
|
||||
}
|
||||
|
||||
static void td028ttec1_panel_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n");
|
||||
|
||||
jbt_ret_write_0(ddata, JBT_REG_DISPLAY_OFF);
|
||||
jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002);
|
||||
jbt_ret_write_0(ddata, JBT_REG_SLEEP_IN);
|
||||
jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x00);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void td028ttec1_panel_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int td028ttec1_panel_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver td028ttec1_ops = {
|
||||
.connect = td028ttec1_panel_connect,
|
||||
.disconnect = td028ttec1_panel_disconnect,
|
||||
|
||||
.enable = td028ttec1_panel_enable,
|
||||
.disable = td028ttec1_panel_disable,
|
||||
|
||||
.set_timings = td028ttec1_panel_set_timings,
|
||||
.get_timings = td028ttec1_panel_get_timings,
|
||||
.check_timings = td028ttec1_panel_check_timings,
|
||||
};
|
||||
|
||||
static int td028ttec1_panel_probe_pdata(struct spi_device *spi)
|
||||
{
|
||||
const struct panel_tpo_td028ttec1_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&spi->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int td028ttec1_probe_of(struct spi_device *spi)
|
||||
{
|
||||
struct device_node *node = spi->dev.of_node;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *in;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&spi->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int td028ttec1_panel_probe(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
dev_dbg(&spi->dev, "%s\n", __func__);
|
||||
|
||||
spi->bits_per_word = 9;
|
||||
spi->mode = SPI_MODE_3;
|
||||
|
||||
r = spi_setup(spi);
|
||||
if (r < 0) {
|
||||
dev_err(&spi->dev, "spi_setup failed: %d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ddata);
|
||||
|
||||
ddata->spi_dev = spi;
|
||||
|
||||
if (dev_get_platdata(&spi->dev)) {
|
||||
r = td028ttec1_panel_probe_pdata(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (spi->dev.of_node) {
|
||||
r = td028ttec1_probe_of(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->videomode = td028ttec1_panel_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &spi->dev;
|
||||
dssdev->driver = &td028ttec1_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
dssdev->phy.dpi.data_lines = ddata->data_lines;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int td028ttec1_panel_remove(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(&ddata->spi_dev->dev, "%s\n", __func__);
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
td028ttec1_panel_disable(dssdev);
|
||||
td028ttec1_panel_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id td028ttec1_of_match[] = {
|
||||
{ .compatible = "omapdss,toppoly,td028ttec1", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, td028ttec1_of_match);
|
||||
|
||||
static struct spi_driver td028ttec1_spi_driver = {
|
||||
.probe = td028ttec1_panel_probe,
|
||||
.remove = td028ttec1_panel_remove,
|
||||
|
||||
.driver = {
|
||||
.name = "panel-tpo-td028ttec1",
|
||||
.of_match_table = td028ttec1_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_spi_driver(td028ttec1_spi_driver);
|
||||
|
||||
MODULE_ALIAS("spi:toppoly,td028ttec1");
|
||||
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
|
||||
MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,686 @@
|
|||
/*
|
||||
* TPO TD043MTEA1 Panel driver
|
||||
*
|
||||
* Author: Gražvydas Ignotas <notasas@gmail.com>
|
||||
* Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
#define TPO_R02_MODE(x) ((x) & 7)
|
||||
#define TPO_R02_MODE_800x480 7
|
||||
#define TPO_R02_NCLK_RISING BIT(3)
|
||||
#define TPO_R02_HSYNC_HIGH BIT(4)
|
||||
#define TPO_R02_VSYNC_HIGH BIT(5)
|
||||
|
||||
#define TPO_R03_NSTANDBY BIT(0)
|
||||
#define TPO_R03_EN_CP_CLK BIT(1)
|
||||
#define TPO_R03_EN_VGL_PUMP BIT(2)
|
||||
#define TPO_R03_EN_PWM BIT(3)
|
||||
#define TPO_R03_DRIVING_CAP_100 BIT(4)
|
||||
#define TPO_R03_EN_PRE_CHARGE BIT(6)
|
||||
#define TPO_R03_SOFTWARE_CTL BIT(7)
|
||||
|
||||
#define TPO_R04_NFLIP_H BIT(0)
|
||||
#define TPO_R04_NFLIP_V BIT(1)
|
||||
#define TPO_R04_CP_CLK_FREQ_1H BIT(2)
|
||||
#define TPO_R04_VGL_FREQ_1H BIT(4)
|
||||
|
||||
#define TPO_R03_VAL_NORMAL (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | \
|
||||
TPO_R03_EN_VGL_PUMP | TPO_R03_EN_PWM | \
|
||||
TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
|
||||
TPO_R03_SOFTWARE_CTL)
|
||||
|
||||
#define TPO_R03_VAL_STANDBY (TPO_R03_DRIVING_CAP_100 | \
|
||||
TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL)
|
||||
|
||||
static const u16 tpo_td043_def_gamma[12] = {
|
||||
105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
|
||||
};
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
int data_lines;
|
||||
|
||||
struct spi_device *spi;
|
||||
struct regulator *vcc_reg;
|
||||
int nreset_gpio;
|
||||
u16 gamma[12];
|
||||
u32 mode;
|
||||
u32 hmirror:1;
|
||||
u32 vmirror:1;
|
||||
u32 powered_on:1;
|
||||
u32 spi_suspended:1;
|
||||
u32 power_on_resume:1;
|
||||
};
|
||||
|
||||
static const struct omap_video_timings tpo_td043_timings = {
|
||||
.x_res = 800,
|
||||
.y_res = 480,
|
||||
|
||||
.pixelclock = 36000000,
|
||||
|
||||
.hsw = 1,
|
||||
.hfp = 68,
|
||||
.hbp = 214,
|
||||
|
||||
.vsw = 1,
|
||||
.vfp = 39,
|
||||
.vbp = 34,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data)
|
||||
{
|
||||
struct spi_message m;
|
||||
struct spi_transfer xfer;
|
||||
u16 w;
|
||||
int r;
|
||||
|
||||
spi_message_init(&m);
|
||||
|
||||
memset(&xfer, 0, sizeof(xfer));
|
||||
|
||||
w = ((u16)addr << 10) | (1 << 8) | data;
|
||||
xfer.tx_buf = &w;
|
||||
xfer.bits_per_word = 16;
|
||||
xfer.len = 2;
|
||||
spi_message_add_tail(&xfer, &m);
|
||||
|
||||
r = spi_sync(spi, &m);
|
||||
if (r < 0)
|
||||
dev_warn(&spi->dev, "failed to write to LCD reg (%d)\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void tpo_td043_write_gamma(struct spi_device *spi, u16 gamma[12])
|
||||
{
|
||||
u8 i, val;
|
||||
|
||||
/* gamma bits [9:8] */
|
||||
for (val = i = 0; i < 4; i++)
|
||||
val |= (gamma[i] & 0x300) >> ((i + 1) * 2);
|
||||
tpo_td043_write(spi, 0x11, val);
|
||||
|
||||
for (val = i = 0; i < 4; i++)
|
||||
val |= (gamma[i+4] & 0x300) >> ((i + 1) * 2);
|
||||
tpo_td043_write(spi, 0x12, val);
|
||||
|
||||
for (val = i = 0; i < 4; i++)
|
||||
val |= (gamma[i+8] & 0x300) >> ((i + 1) * 2);
|
||||
tpo_td043_write(spi, 0x13, val);
|
||||
|
||||
/* gamma bits [7:0] */
|
||||
for (val = i = 0; i < 12; i++)
|
||||
tpo_td043_write(spi, 0x14 + i, gamma[i] & 0xff);
|
||||
}
|
||||
|
||||
static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v)
|
||||
{
|
||||
u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V |
|
||||
TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H;
|
||||
if (h)
|
||||
reg4 &= ~TPO_R04_NFLIP_H;
|
||||
if (v)
|
||||
reg4 &= ~TPO_R04_NFLIP_V;
|
||||
|
||||
return tpo_td043_write(spi, 4, reg4);
|
||||
}
|
||||
|
||||
static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
|
||||
|
||||
ddata->hmirror = enable;
|
||||
return tpo_td043_write_mirror(ddata->spi, ddata->hmirror,
|
||||
ddata->vmirror);
|
||||
}
|
||||
|
||||
static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
|
||||
|
||||
return ddata->hmirror;
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_vmirror_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ddata->vmirror);
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_vmirror_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
int val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoint(buf, 0, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = !!val;
|
||||
|
||||
ret = tpo_td043_write_mirror(ddata->spi, ddata->hmirror, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ddata->vmirror = val;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ddata->mode);
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_mode_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 0, &val);
|
||||
if (ret != 0 || val & ~7)
|
||||
return -EINVAL;
|
||||
|
||||
ddata->mode = val;
|
||||
|
||||
val |= TPO_R02_NCLK_RISING;
|
||||
tpo_td043_write(ddata->spi, 2, val);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_gamma_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
ssize_t len = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ddata->gamma); i++) {
|
||||
ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
|
||||
ddata->gamma[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
len += ret;
|
||||
}
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_gamma_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
unsigned int g[12];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u",
|
||||
&g[0], &g[1], &g[2], &g[3], &g[4], &g[5],
|
||||
&g[6], &g[7], &g[8], &g[9], &g[10], &g[11]);
|
||||
|
||||
if (ret != 12)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < 12; i++)
|
||||
ddata->gamma[i] = g[i];
|
||||
|
||||
tpo_td043_write_gamma(ddata->spi, ddata->gamma);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(vmirror, S_IRUGO | S_IWUSR,
|
||||
tpo_td043_vmirror_show, tpo_td043_vmirror_store);
|
||||
static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
|
||||
tpo_td043_mode_show, tpo_td043_mode_store);
|
||||
static DEVICE_ATTR(gamma, S_IRUGO | S_IWUSR,
|
||||
tpo_td043_gamma_show, tpo_td043_gamma_store);
|
||||
|
||||
static struct attribute *tpo_td043_attrs[] = {
|
||||
&dev_attr_vmirror.attr,
|
||||
&dev_attr_mode.attr,
|
||||
&dev_attr_gamma.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group tpo_td043_attr_group = {
|
||||
.attrs = tpo_td043_attrs,
|
||||
};
|
||||
|
||||
static int tpo_td043_power_on(struct panel_drv_data *ddata)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (ddata->powered_on)
|
||||
return 0;
|
||||
|
||||
r = regulator_enable(ddata->vcc_reg);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
/* wait for panel to stabilize */
|
||||
msleep(160);
|
||||
|
||||
if (gpio_is_valid(ddata->nreset_gpio))
|
||||
gpio_set_value(ddata->nreset_gpio, 1);
|
||||
|
||||
tpo_td043_write(ddata->spi, 2,
|
||||
TPO_R02_MODE(ddata->mode) | TPO_R02_NCLK_RISING);
|
||||
tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_NORMAL);
|
||||
tpo_td043_write(ddata->spi, 0x20, 0xf0);
|
||||
tpo_td043_write(ddata->spi, 0x21, 0xf0);
|
||||
tpo_td043_write_mirror(ddata->spi, ddata->hmirror,
|
||||
ddata->vmirror);
|
||||
tpo_td043_write_gamma(ddata->spi, ddata->gamma);
|
||||
|
||||
ddata->powered_on = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpo_td043_power_off(struct panel_drv_data *ddata)
|
||||
{
|
||||
if (!ddata->powered_on)
|
||||
return;
|
||||
|
||||
tpo_td043_write(ddata->spi, 3,
|
||||
TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
|
||||
|
||||
if (gpio_is_valid(ddata->nreset_gpio))
|
||||
gpio_set_value(ddata->nreset_gpio, 0);
|
||||
|
||||
/* wait for at least 2 vsyncs before cutting off power */
|
||||
msleep(50);
|
||||
|
||||
tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_STANDBY);
|
||||
|
||||
regulator_disable(ddata->vcc_reg);
|
||||
|
||||
ddata->powered_on = 0;
|
||||
}
|
||||
|
||||
static int tpo_td043_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpo_td043_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int tpo_td043_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* If we are resuming from system suspend, SPI clocks might not be
|
||||
* enabled yet, so we'll program the LCD from SPI PM resume callback.
|
||||
*/
|
||||
if (!ddata->spi_suspended) {
|
||||
r = tpo_td043_power_on(ddata);
|
||||
if (r) {
|
||||
in->ops.dpi->disable(in);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpo_td043_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
if (!ddata->spi_suspended)
|
||||
tpo_td043_power_off(ddata);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void tpo_td043_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void tpo_td043_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int tpo_td043_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver tpo_td043_ops = {
|
||||
.connect = tpo_td043_connect,
|
||||
.disconnect = tpo_td043_disconnect,
|
||||
|
||||
.enable = tpo_td043_enable,
|
||||
.disable = tpo_td043_disable,
|
||||
|
||||
.set_timings = tpo_td043_set_timings,
|
||||
.get_timings = tpo_td043_get_timings,
|
||||
.check_timings = tpo_td043_check_timings,
|
||||
|
||||
.set_mirror = tpo_td043_set_hmirror,
|
||||
.get_mirror = tpo_td043_get_hmirror,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
|
||||
static int tpo_td043_probe_pdata(struct spi_device *spi)
|
||||
{
|
||||
const struct panel_tpo_td043mtea1_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
ddata->nreset_gpio = pdata->nreset_gpio;
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&spi->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpo_td043_probe_of(struct spi_device *spi)
|
||||
{
|
||||
struct device_node *node = spi->dev.of_node;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *in;
|
||||
int gpio;
|
||||
|
||||
gpio = of_get_named_gpio(node, "reset-gpios", 0);
|
||||
if (!gpio_is_valid(gpio)) {
|
||||
dev_err(&spi->dev, "failed to parse enable gpio\n");
|
||||
return gpio;
|
||||
}
|
||||
ddata->nreset_gpio = gpio;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&spi->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpo_td043_probe(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
dev_dbg(&spi->dev, "%s\n", __func__);
|
||||
|
||||
spi->bits_per_word = 16;
|
||||
spi->mode = SPI_MODE_0;
|
||||
|
||||
r = spi_setup(spi);
|
||||
if (r < 0) {
|
||||
dev_err(&spi->dev, "spi_setup failed: %d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ddata);
|
||||
|
||||
ddata->spi = spi;
|
||||
|
||||
if (dev_get_platdata(&spi->dev)) {
|
||||
r = tpo_td043_probe_pdata(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (spi->dev.of_node) {
|
||||
r = tpo_td043_probe_of(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->mode = TPO_R02_MODE_800x480;
|
||||
memcpy(ddata->gamma, tpo_td043_def_gamma, sizeof(ddata->gamma));
|
||||
|
||||
ddata->vcc_reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (IS_ERR(ddata->vcc_reg)) {
|
||||
dev_err(&spi->dev, "failed to get LCD VCC regulator\n");
|
||||
r = PTR_ERR(ddata->vcc_reg);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->nreset_gpio)) {
|
||||
r = devm_gpio_request_one(&spi->dev,
|
||||
ddata->nreset_gpio, GPIOF_OUT_INIT_LOW,
|
||||
"lcd reset");
|
||||
if (r < 0) {
|
||||
dev_err(&spi->dev, "couldn't request reset GPIO\n");
|
||||
goto err_gpio_req;
|
||||
}
|
||||
}
|
||||
|
||||
r = sysfs_create_group(&spi->dev.kobj, &tpo_td043_attr_group);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "failed to create sysfs files\n");
|
||||
goto err_sysfs;
|
||||
}
|
||||
|
||||
ddata->videomode = tpo_td043_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &spi->dev;
|
||||
dssdev->driver = &tpo_td043_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
|
||||
err_sysfs:
|
||||
err_gpio_req:
|
||||
err_regulator:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int tpo_td043_remove(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
tpo_td043_disable(dssdev);
|
||||
tpo_td043_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tpo_td043_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", ddata);
|
||||
|
||||
ddata->power_on_resume = ddata->powered_on;
|
||||
tpo_td043_power_off(ddata);
|
||||
ddata->spi_suspended = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpo_td043_spi_resume(struct device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "tpo_td043_spi_resume\n");
|
||||
|
||||
if (ddata->power_on_resume) {
|
||||
ret = tpo_td043_power_on(ddata);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ddata->spi_suspended = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm,
|
||||
tpo_td043_spi_suspend, tpo_td043_spi_resume);
|
||||
|
||||
static const struct of_device_id tpo_td043_of_match[] = {
|
||||
{ .compatible = "omapdss,tpo,td043mtea1", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, tpo_td043_of_match);
|
||||
|
||||
static struct spi_driver tpo_td043_spi_driver = {
|
||||
.driver = {
|
||||
.name = "panel-tpo-td043mtea1",
|
||||
.pm = &tpo_td043_spi_pm,
|
||||
.of_match_table = tpo_td043_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = tpo_td043_probe,
|
||||
.remove = tpo_td043_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(tpo_td043_spi_driver);
|
||||
|
||||
MODULE_ALIAS("spi:tpo,td043mtea1");
|
||||
MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
|
||||
MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,129 @@
|
|||
config FB_OMAP2_DSS_INIT
|
||||
bool
|
||||
|
||||
config FB_OMAP2_DSS
|
||||
tristate
|
||||
select VIDEOMODE_HELPERS
|
||||
select FB_OMAP2_DSS_INIT
|
||||
select HDMI
|
||||
|
||||
config FB_OMAP2_DSS_DEBUG
|
||||
bool "Debug support"
|
||||
default n
|
||||
help
|
||||
This enables printing of debug messages. Alternatively, debug messages
|
||||
can also be enabled by setting CONFIG_DYNAMIC_DEBUG and then setting
|
||||
appropriate flags in <debugfs>/dynamic_debug/control.
|
||||
|
||||
config FB_OMAP2_DSS_DEBUGFS
|
||||
bool "Debugfs filesystem support"
|
||||
depends on DEBUG_FS
|
||||
default n
|
||||
help
|
||||
This enables debugfs for OMAPDSS at <debugfs>/omapdss. This enables
|
||||
querying about clock configuration and register configuration of dss,
|
||||
dispc, dsi, hdmi and rfbi.
|
||||
|
||||
config FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
bool "Collect DSS IRQ statistics"
|
||||
depends on FB_OMAP2_DSS_DEBUGFS
|
||||
default n
|
||||
help
|
||||
Collect DSS IRQ statistics, printable via debugfs.
|
||||
|
||||
The statistics can be found from
|
||||
<debugfs>/omapdss/dispc_irq for DISPC interrupts, and
|
||||
<debugfs>/omapdss/dsi_irq for DSI interrupts.
|
||||
|
||||
config FB_OMAP2_DSS_DPI
|
||||
bool "DPI support"
|
||||
default y
|
||||
help
|
||||
DPI Interface. This is the Parallel Display Interface.
|
||||
|
||||
config FB_OMAP2_DSS_RFBI
|
||||
bool "RFBI support"
|
||||
depends on BROKEN
|
||||
default n
|
||||
help
|
||||
MIPI DBI support (RFBI, Remote Framebuffer Interface, in Texas
|
||||
Instrument's terminology).
|
||||
|
||||
DBI is a bus between the host processor and a peripheral,
|
||||
such as a display or a framebuffer chip.
|
||||
|
||||
See http://www.mipi.org/ for DBI specifications.
|
||||
|
||||
config FB_OMAP2_DSS_VENC
|
||||
bool "VENC support"
|
||||
default y
|
||||
help
|
||||
OMAP Video Encoder support for S-Video and composite TV-out.
|
||||
|
||||
config FB_OMAP2_DSS_HDMI_COMMON
|
||||
bool
|
||||
|
||||
config FB_OMAP4_DSS_HDMI
|
||||
bool "HDMI support for OMAP4"
|
||||
default y
|
||||
select FB_OMAP2_DSS_HDMI_COMMON
|
||||
help
|
||||
HDMI support for OMAP4 based SoCs.
|
||||
|
||||
config FB_OMAP5_DSS_HDMI
|
||||
bool "HDMI support for OMAP5"
|
||||
default n
|
||||
select FB_OMAP2_DSS_HDMI_COMMON
|
||||
help
|
||||
HDMI Interface for OMAP5 and similar cores. This adds the High
|
||||
Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI
|
||||
specification.
|
||||
|
||||
config FB_OMAP2_DSS_SDI
|
||||
bool "SDI support"
|
||||
default n
|
||||
help
|
||||
SDI (Serial Display Interface) support.
|
||||
|
||||
SDI is a high speed one-way display serial bus between the host
|
||||
processor and a display.
|
||||
|
||||
config FB_OMAP2_DSS_DSI
|
||||
bool "DSI support"
|
||||
default n
|
||||
help
|
||||
MIPI DSI (Display Serial Interface) support.
|
||||
|
||||
DSI is a high speed half-duplex serial interface between the host
|
||||
processor and a peripheral, such as a display or a framebuffer chip.
|
||||
|
||||
See http://www.mipi.org/ for DSI specifications.
|
||||
|
||||
config FB_OMAP2_DSS_MIN_FCK_PER_PCK
|
||||
int "Minimum FCK/PCK ratio (for scaling)"
|
||||
range 0 32
|
||||
default 0
|
||||
help
|
||||
This can be used to adjust the minimum FCK/PCK ratio.
|
||||
|
||||
With this you can make sure that DISPC FCK is at least
|
||||
n x PCK. Video plane scaling requires higher FCK than
|
||||
normally.
|
||||
|
||||
If this is set to 0, there's no extra constraint on the
|
||||
DISPC FCK. However, the FCK will at minimum be
|
||||
2xPCK (if active matrix) or 3xPCK (if passive matrix).
|
||||
|
||||
Max FCK is 173MHz, so this doesn't work if your PCK
|
||||
is very high.
|
||||
|
||||
config FB_OMAP2_DSS_SLEEP_AFTER_VENC_RESET
|
||||
bool "Sleep 20ms after VENC reset"
|
||||
default y
|
||||
help
|
||||
There is a 20ms sleep after VENC reset which seemed to fix the
|
||||
reset. The reason for the bug is unclear, and it's also unclear
|
||||
on what platforms this happens.
|
||||
|
||||
This option enables the sleep, and is enabled by default. You can
|
||||
disable the sleep if it doesn't cause problems on your platform.
|
|
@ -0,0 +1,18 @@
|
|||
obj-$(CONFIG_FB_OMAP2_DSS_INIT) += omapdss-boot-init.o
|
||||
obj-$(CONFIG_FB_OMAP2_DSS) += omapdss.o
|
||||
# Core DSS files
|
||||
omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
|
||||
output.o dss-of.o pll.o video-pll.o
|
||||
# DSS compat layer files
|
||||
omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \
|
||||
dispc-compat.o display-sysfs.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_DPI) += dpi.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_RFBI) += rfbi.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_VENC) += venc.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_SDI) += sdi.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_DSI) += dsi.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_HDMI_COMMON) += hdmi_common.o hdmi_wp.o hdmi_pll.o \
|
||||
hdmi_phy.o
|
||||
omapdss-$(CONFIG_FB_OMAP4_DSS_HDMI) += hdmi4.o hdmi4_core.o
|
||||
omapdss-$(CONFIG_FB_OMAP5_DSS_HDMI) += hdmi5.o hdmi5_core.o
|
||||
ccflags-$(CONFIG_FB_OMAP2_DSS_DEBUG) += -DDEBUG
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* linux/drivers/video/omap2/dss/core.c
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
||||
*
|
||||
* Some code and ideas taken from drivers/video/omap/ driver
|
||||
* by Imre Deak.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define DSS_SUBSYS_NAME "CORE"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
|
||||
#include "dss.h"
|
||||
#include "dss_features.h"
|
||||
|
||||
static struct {
|
||||
struct platform_device *pdev;
|
||||
|
||||
const char *default_display_name;
|
||||
} core;
|
||||
|
||||
static char *def_disp_name;
|
||||
module_param_named(def_disp, def_disp_name, charp, 0);
|
||||
MODULE_PARM_DESC(def_disp, "default display name");
|
||||
|
||||
const char *omapdss_get_default_display_name(void)
|
||||
{
|
||||
return core.default_display_name;
|
||||
}
|
||||
EXPORT_SYMBOL(omapdss_get_default_display_name);
|
||||
|
||||
enum omapdss_version omapdss_get_version(void)
|
||||
{
|
||||
struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
|
||||
return pdata->version;
|
||||
}
|
||||
EXPORT_SYMBOL(omapdss_get_version);
|
||||
|
||||
struct platform_device *dss_get_core_pdev(void)
|
||||
{
|
||||
return core.pdev;
|
||||
}
|
||||
|
||||
int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask)
|
||||
{
|
||||
struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
|
||||
|
||||
if (!board_data->dsi_enable_pads)
|
||||
return -ENOENT;
|
||||
|
||||
return board_data->dsi_enable_pads(dsi_id, lane_mask);
|
||||
}
|
||||
|
||||
void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask)
|
||||
{
|
||||
struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
|
||||
|
||||
if (!board_data->dsi_disable_pads)
|
||||
return;
|
||||
|
||||
return board_data->dsi_disable_pads(dsi_id, lane_mask);
|
||||
}
|
||||
|
||||
int dss_set_min_bus_tput(struct device *dev, unsigned long tput)
|
||||
{
|
||||
struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
|
||||
|
||||
if (pdata->set_min_bus_tput)
|
||||
return pdata->set_min_bus_tput(dev, tput);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FB_OMAP2_DSS_DEBUGFS)
|
||||
static int dss_debug_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
void (*func)(struct seq_file *) = s->private;
|
||||
func(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dss_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dss_debug_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations dss_debug_fops = {
|
||||
.open = dss_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static struct dentry *dss_debugfs_dir;
|
||||
|
||||
static int dss_initialize_debugfs(void)
|
||||
{
|
||||
dss_debugfs_dir = debugfs_create_dir("omapdss", NULL);
|
||||
if (IS_ERR(dss_debugfs_dir)) {
|
||||
int err = PTR_ERR(dss_debugfs_dir);
|
||||
dss_debugfs_dir = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
|
||||
&dss_debug_dump_clocks, &dss_debug_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dss_uninitialize_debugfs(void)
|
||||
{
|
||||
if (dss_debugfs_dir)
|
||||
debugfs_remove_recursive(dss_debugfs_dir);
|
||||
}
|
||||
|
||||
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
|
||||
{
|
||||
struct dentry *d;
|
||||
|
||||
d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir,
|
||||
write, &dss_debug_fops);
|
||||
|
||||
return PTR_ERR_OR_ZERO(d);
|
||||
}
|
||||
#else /* CONFIG_FB_OMAP2_DSS_DEBUGFS */
|
||||
static inline int dss_initialize_debugfs(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void dss_uninitialize_debugfs(void)
|
||||
{
|
||||
}
|
||||
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_FB_OMAP2_DSS_DEBUGFS */
|
||||
|
||||
/* PLATFORM DEVICE */
|
||||
static int omap_dss_pm_notif(struct notifier_block *b, unsigned long v, void *d)
|
||||
{
|
||||
DSSDBG("pm notif %lu\n", v);
|
||||
|
||||
switch (v) {
|
||||
case PM_SUSPEND_PREPARE:
|
||||
case PM_HIBERNATION_PREPARE:
|
||||
case PM_RESTORE_PREPARE:
|
||||
DSSDBG("suspending displays\n");
|
||||
return dss_suspend_all_devices();
|
||||
|
||||
case PM_POST_SUSPEND:
|
||||
case PM_POST_HIBERNATION:
|
||||
case PM_POST_RESTORE:
|
||||
DSSDBG("resuming displays\n");
|
||||
return dss_resume_all_devices();
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct notifier_block omap_dss_pm_notif_block = {
|
||||
.notifier_call = omap_dss_pm_notif,
|
||||
};
|
||||
|
||||
static int __init omap_dss_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
|
||||
int r;
|
||||
|
||||
core.pdev = pdev;
|
||||
|
||||
dss_features_init(omapdss_get_version());
|
||||
|
||||
r = dss_initialize_debugfs();
|
||||
if (r)
|
||||
goto err_debugfs;
|
||||
|
||||
if (def_disp_name)
|
||||
core.default_display_name = def_disp_name;
|
||||
else if (pdata->default_display_name)
|
||||
core.default_display_name = pdata->default_display_name;
|
||||
else if (pdata->default_device)
|
||||
core.default_display_name = pdata->default_device->name;
|
||||
|
||||
register_pm_notifier(&omap_dss_pm_notif_block);
|
||||
|
||||
return 0;
|
||||
|
||||
err_debugfs:
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int omap_dss_remove(struct platform_device *pdev)
|
||||
{
|
||||
unregister_pm_notifier(&omap_dss_pm_notif_block);
|
||||
|
||||
dss_uninitialize_debugfs();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_dss_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
DSSDBG("shutdown\n");
|
||||
dss_disable_all_devices();
|
||||
}
|
||||
|
||||
static struct platform_driver omap_dss_driver = {
|
||||
.remove = omap_dss_remove,
|
||||
.shutdown = omap_dss_shutdown,
|
||||
.driver = {
|
||||
.name = "omapdss",
|
||||
},
|
||||
};
|
||||
|
||||
/* INIT */
|
||||
static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
|
||||
dss_init_platform_driver,
|
||||
dispc_init_platform_driver,
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_DSI
|
||||
dsi_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_DPI
|
||||
dpi_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_SDI
|
||||
sdi_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_RFBI
|
||||
rfbi_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_VENC
|
||||
venc_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP4_DSS_HDMI
|
||||
hdmi4_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP5_DSS_HDMI
|
||||
hdmi5_init_platform_driver,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void (*dss_output_drv_unreg_funcs[])(void) = {
|
||||
#ifdef CONFIG_FB_OMAP5_DSS_HDMI
|
||||
hdmi5_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP4_DSS_HDMI
|
||||
hdmi4_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_VENC
|
||||
venc_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_RFBI
|
||||
rfbi_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_SDI
|
||||
sdi_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_DPI
|
||||
dpi_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_DSI
|
||||
dsi_uninit_platform_driver,
|
||||
#endif
|
||||
dispc_uninit_platform_driver,
|
||||
dss_uninit_platform_driver,
|
||||
};
|
||||
|
||||
static int __init omap_dss_init(void)
|
||||
{
|
||||
int r;
|
||||
int i;
|
||||
|
||||
r = platform_driver_probe(&omap_dss_driver, omap_dss_probe);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) {
|
||||
r = dss_output_drv_reg_funcs[i]();
|
||||
if (r)
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
for (i = ARRAY_SIZE(dss_output_drv_reg_funcs) - i;
|
||||
i < ARRAY_SIZE(dss_output_drv_reg_funcs);
|
||||
++i)
|
||||
dss_output_drv_unreg_funcs[i]();
|
||||
|
||||
platform_driver_unregister(&omap_dss_driver);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __exit omap_dss_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i)
|
||||
dss_output_drv_unreg_funcs[i]();
|
||||
|
||||
platform_driver_unregister(&omap_dss_driver);
|
||||
}
|
||||
|
||||
module_init(omap_dss_init);
|
||||
module_exit(omap_dss_exit);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
|
||||
MODULE_DESCRIPTION("OMAP2/3 Display Subsystem");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -0,0 +1,667 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define DSS_SUBSYS_NAME "APPLY"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
|
||||
#include "dss.h"
|
||||
#include "dss_features.h"
|
||||
#include "dispc-compat.h"
|
||||
|
||||
#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
|
||||
DISPC_IRQ_OCP_ERR | \
|
||||
DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
|
||||
DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
|
||||
DISPC_IRQ_SYNC_LOST | \
|
||||
DISPC_IRQ_SYNC_LOST_DIGIT)
|
||||
|
||||
#define DISPC_MAX_NR_ISRS 8
|
||||
|
||||
struct omap_dispc_isr_data {
|
||||
omap_dispc_isr_t isr;
|
||||
void *arg;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
struct dispc_irq_stats {
|
||||
unsigned long last_reset;
|
||||
unsigned irq_count;
|
||||
unsigned irqs[32];
|
||||
};
|
||||
|
||||
static struct {
|
||||
spinlock_t irq_lock;
|
||||
u32 irq_error_mask;
|
||||
struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
|
||||
u32 error_irqs;
|
||||
struct work_struct error_work;
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
spinlock_t irq_stats_lock;
|
||||
struct dispc_irq_stats irq_stats;
|
||||
#endif
|
||||
} dispc_compat;
|
||||
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
static void dispc_dump_irqs(struct seq_file *s)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct dispc_irq_stats stats;
|
||||
|
||||
spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags);
|
||||
|
||||
stats = dispc_compat.irq_stats;
|
||||
memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats));
|
||||
dispc_compat.irq_stats.last_reset = jiffies;
|
||||
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags);
|
||||
|
||||
seq_printf(s, "period %u ms\n",
|
||||
jiffies_to_msecs(jiffies - stats.last_reset));
|
||||
|
||||
seq_printf(s, "irqs %d\n", stats.irq_count);
|
||||
#define PIS(x) \
|
||||
seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]);
|
||||
|
||||
PIS(FRAMEDONE);
|
||||
PIS(VSYNC);
|
||||
PIS(EVSYNC_EVEN);
|
||||
PIS(EVSYNC_ODD);
|
||||
PIS(ACBIAS_COUNT_STAT);
|
||||
PIS(PROG_LINE_NUM);
|
||||
PIS(GFX_FIFO_UNDERFLOW);
|
||||
PIS(GFX_END_WIN);
|
||||
PIS(PAL_GAMMA_MASK);
|
||||
PIS(OCP_ERR);
|
||||
PIS(VID1_FIFO_UNDERFLOW);
|
||||
PIS(VID1_END_WIN);
|
||||
PIS(VID2_FIFO_UNDERFLOW);
|
||||
PIS(VID2_END_WIN);
|
||||
if (dss_feat_get_num_ovls() > 3) {
|
||||
PIS(VID3_FIFO_UNDERFLOW);
|
||||
PIS(VID3_END_WIN);
|
||||
}
|
||||
PIS(SYNC_LOST);
|
||||
PIS(SYNC_LOST_DIGIT);
|
||||
PIS(WAKEUP);
|
||||
if (dss_has_feature(FEAT_MGR_LCD2)) {
|
||||
PIS(FRAMEDONE2);
|
||||
PIS(VSYNC2);
|
||||
PIS(ACBIAS_COUNT_STAT2);
|
||||
PIS(SYNC_LOST2);
|
||||
}
|
||||
if (dss_has_feature(FEAT_MGR_LCD3)) {
|
||||
PIS(FRAMEDONE3);
|
||||
PIS(VSYNC3);
|
||||
PIS(ACBIAS_COUNT_STAT3);
|
||||
PIS(SYNC_LOST3);
|
||||
}
|
||||
#undef PIS
|
||||
}
|
||||
#endif
|
||||
|
||||
/* dispc.irq_lock has to be locked by the caller */
|
||||
static void _omap_dispc_set_irqs(void)
|
||||
{
|
||||
u32 mask;
|
||||
int i;
|
||||
struct omap_dispc_isr_data *isr_data;
|
||||
|
||||
mask = dispc_compat.irq_error_mask;
|
||||
|
||||
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||||
isr_data = &dispc_compat.registered_isr[i];
|
||||
|
||||
if (isr_data->isr == NULL)
|
||||
continue;
|
||||
|
||||
mask |= isr_data->mask;
|
||||
}
|
||||
|
||||
dispc_write_irqenable(mask);
|
||||
}
|
||||
|
||||
int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
struct omap_dispc_isr_data *isr_data;
|
||||
|
||||
if (isr == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||||
|
||||
/* check for duplicate entry */
|
||||
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||||
isr_data = &dispc_compat.registered_isr[i];
|
||||
if (isr_data->isr == isr && isr_data->arg == arg &&
|
||||
isr_data->mask == mask) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
isr_data = NULL;
|
||||
ret = -EBUSY;
|
||||
|
||||
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||||
isr_data = &dispc_compat.registered_isr[i];
|
||||
|
||||
if (isr_data->isr != NULL)
|
||||
continue;
|
||||
|
||||
isr_data->isr = isr;
|
||||
isr_data->arg = arg;
|
||||
isr_data->mask = mask;
|
||||
ret = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
_omap_dispc_set_irqs();
|
||||
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_dispc_register_isr);
|
||||
|
||||
int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
int ret = -EINVAL;
|
||||
struct omap_dispc_isr_data *isr_data;
|
||||
|
||||
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||||
|
||||
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||||
isr_data = &dispc_compat.registered_isr[i];
|
||||
if (isr_data->isr != isr || isr_data->arg != arg ||
|
||||
isr_data->mask != mask)
|
||||
continue;
|
||||
|
||||
/* found the correct isr */
|
||||
|
||||
isr_data->isr = NULL;
|
||||
isr_data->arg = NULL;
|
||||
isr_data->mask = 0;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
_omap_dispc_set_irqs();
|
||||
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_dispc_unregister_isr);
|
||||
|
||||
static void print_irq_status(u32 status)
|
||||
{
|
||||
if ((status & dispc_compat.irq_error_mask) == 0)
|
||||
return;
|
||||
|
||||
#define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : ""
|
||||
|
||||
pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n",
|
||||
status,
|
||||
PIS(OCP_ERR),
|
||||
PIS(GFX_FIFO_UNDERFLOW),
|
||||
PIS(VID1_FIFO_UNDERFLOW),
|
||||
PIS(VID2_FIFO_UNDERFLOW),
|
||||
dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "",
|
||||
PIS(SYNC_LOST),
|
||||
PIS(SYNC_LOST_DIGIT),
|
||||
dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "",
|
||||
dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : "");
|
||||
#undef PIS
|
||||
}
|
||||
|
||||
/* Called from dss.c. Note that we don't touch clocks here,
|
||||
* but we presume they are on because we got an IRQ. However,
|
||||
* an irq handler may turn the clocks off, so we may not have
|
||||
* clock later in the function. */
|
||||
static irqreturn_t omap_dispc_irq_handler(int irq, void *arg)
|
||||
{
|
||||
int i;
|
||||
u32 irqstatus, irqenable;
|
||||
u32 handledirqs = 0;
|
||||
u32 unhandled_errors;
|
||||
struct omap_dispc_isr_data *isr_data;
|
||||
struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
|
||||
|
||||
spin_lock(&dispc_compat.irq_lock);
|
||||
|
||||
irqstatus = dispc_read_irqstatus();
|
||||
irqenable = dispc_read_irqenable();
|
||||
|
||||
/* IRQ is not for us */
|
||||
if (!(irqstatus & irqenable)) {
|
||||
spin_unlock(&dispc_compat.irq_lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
spin_lock(&dispc_compat.irq_stats_lock);
|
||||
dispc_compat.irq_stats.irq_count++;
|
||||
dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs);
|
||||
spin_unlock(&dispc_compat.irq_stats_lock);
|
||||
#endif
|
||||
|
||||
print_irq_status(irqstatus);
|
||||
|
||||
/* Ack the interrupt. Do it here before clocks are possibly turned
|
||||
* off */
|
||||
dispc_clear_irqstatus(irqstatus);
|
||||
/* flush posted write */
|
||||
dispc_read_irqstatus();
|
||||
|
||||
/* make a copy and unlock, so that isrs can unregister
|
||||
* themselves */
|
||||
memcpy(registered_isr, dispc_compat.registered_isr,
|
||||
sizeof(registered_isr));
|
||||
|
||||
spin_unlock(&dispc_compat.irq_lock);
|
||||
|
||||
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||||
isr_data = ®istered_isr[i];
|
||||
|
||||
if (!isr_data->isr)
|
||||
continue;
|
||||
|
||||
if (isr_data->mask & irqstatus) {
|
||||
isr_data->isr(isr_data->arg, irqstatus);
|
||||
handledirqs |= isr_data->mask;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock(&dispc_compat.irq_lock);
|
||||
|
||||
unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask;
|
||||
|
||||
if (unhandled_errors) {
|
||||
dispc_compat.error_irqs |= unhandled_errors;
|
||||
|
||||
dispc_compat.irq_error_mask &= ~unhandled_errors;
|
||||
_omap_dispc_set_irqs();
|
||||
|
||||
schedule_work(&dispc_compat.error_work);
|
||||
}
|
||||
|
||||
spin_unlock(&dispc_compat.irq_lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void dispc_error_worker(struct work_struct *work)
|
||||
{
|
||||
int i;
|
||||
u32 errors;
|
||||
unsigned long flags;
|
||||
static const unsigned fifo_underflow_bits[] = {
|
||||
DISPC_IRQ_GFX_FIFO_UNDERFLOW,
|
||||
DISPC_IRQ_VID1_FIFO_UNDERFLOW,
|
||||
DISPC_IRQ_VID2_FIFO_UNDERFLOW,
|
||||
DISPC_IRQ_VID3_FIFO_UNDERFLOW,
|
||||
};
|
||||
|
||||
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||||
errors = dispc_compat.error_irqs;
|
||||
dispc_compat.error_irqs = 0;
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||||
|
||||
dispc_runtime_get();
|
||||
|
||||
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
|
||||
struct omap_overlay *ovl;
|
||||
unsigned bit;
|
||||
|
||||
ovl = omap_dss_get_overlay(i);
|
||||
bit = fifo_underflow_bits[i];
|
||||
|
||||
if (bit & errors) {
|
||||
DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n",
|
||||
ovl->name);
|
||||
ovl->disable(ovl);
|
||||
msleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
|
||||
struct omap_overlay_manager *mgr;
|
||||
unsigned bit;
|
||||
|
||||
mgr = omap_dss_get_overlay_manager(i);
|
||||
bit = dispc_mgr_get_sync_lost_irq(i);
|
||||
|
||||
if (bit & errors) {
|
||||
int j;
|
||||
|
||||
DSSERR("SYNC_LOST on channel %s, restarting the output "
|
||||
"with video overlays disabled\n",
|
||||
mgr->name);
|
||||
|
||||
dss_mgr_disable(mgr);
|
||||
|
||||
for (j = 0; j < omap_dss_get_num_overlays(); ++j) {
|
||||
struct omap_overlay *ovl;
|
||||
ovl = omap_dss_get_overlay(j);
|
||||
|
||||
if (ovl->id != OMAP_DSS_GFX &&
|
||||
ovl->manager == mgr)
|
||||
ovl->disable(ovl);
|
||||
}
|
||||
|
||||
dss_mgr_enable(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors & DISPC_IRQ_OCP_ERR) {
|
||||
DSSERR("OCP_ERR\n");
|
||||
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
|
||||
struct omap_overlay_manager *mgr;
|
||||
|
||||
mgr = omap_dss_get_overlay_manager(i);
|
||||
dss_mgr_disable(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||||
dispc_compat.irq_error_mask |= errors;
|
||||
_omap_dispc_set_irqs();
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||||
|
||||
dispc_runtime_put();
|
||||
}
|
||||
|
||||
int dss_dispc_initialize_irq(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
spin_lock_init(&dispc_compat.irq_stats_lock);
|
||||
dispc_compat.irq_stats.last_reset = jiffies;
|
||||
dss_debugfs_create_file("dispc_irq", dispc_dump_irqs);
|
||||
#endif
|
||||
|
||||
spin_lock_init(&dispc_compat.irq_lock);
|
||||
|
||||
memset(dispc_compat.registered_isr, 0,
|
||||
sizeof(dispc_compat.registered_isr));
|
||||
|
||||
dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR;
|
||||
if (dss_has_feature(FEAT_MGR_LCD2))
|
||||
dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
|
||||
if (dss_has_feature(FEAT_MGR_LCD3))
|
||||
dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
|
||||
if (dss_feat_get_num_ovls() > 3)
|
||||
dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
|
||||
|
||||
/*
|
||||
* there's SYNC_LOST_DIGIT waiting after enabling the DSS,
|
||||
* so clear it
|
||||
*/
|
||||
dispc_clear_irqstatus(dispc_read_irqstatus());
|
||||
|
||||
INIT_WORK(&dispc_compat.error_work, dispc_error_worker);
|
||||
|
||||
_omap_dispc_set_irqs();
|
||||
|
||||
r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat);
|
||||
if (r) {
|
||||
DSSERR("dispc_request_irq failed\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dss_dispc_uninitialize_irq(void)
|
||||
{
|
||||
dispc_free_irq(&dispc_compat);
|
||||
}
|
||||
|
||||
static void dispc_mgr_disable_isr(void *data, u32 mask)
|
||||
{
|
||||
struct completion *compl = data;
|
||||
complete(compl);
|
||||
}
|
||||
|
||||
static void dispc_mgr_enable_lcd_out(enum omap_channel channel)
|
||||
{
|
||||
dispc_mgr_enable(channel, true);
|
||||
}
|
||||
|
||||
static void dispc_mgr_disable_lcd_out(enum omap_channel channel)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(framedone_compl);
|
||||
int r;
|
||||
u32 irq;
|
||||
|
||||
if (!dispc_mgr_is_enabled(channel))
|
||||
return;
|
||||
|
||||
/*
|
||||
* When we disable LCD output, we need to wait for FRAMEDONE to know
|
||||
* that DISPC has finished with the LCD output.
|
||||
*/
|
||||
|
||||
irq = dispc_mgr_get_framedone_irq(channel);
|
||||
|
||||
r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||||
irq);
|
||||
if (r)
|
||||
DSSERR("failed to register FRAMEDONE isr\n");
|
||||
|
||||
dispc_mgr_enable(channel, false);
|
||||
|
||||
/* if we couldn't register for framedone, just sleep and exit */
|
||||
if (r) {
|
||||
msleep(100);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_timeout(&framedone_compl,
|
||||
msecs_to_jiffies(100)))
|
||||
DSSERR("timeout waiting for FRAME DONE\n");
|
||||
|
||||
r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||||
irq);
|
||||
if (r)
|
||||
DSSERR("failed to unregister FRAMEDONE isr\n");
|
||||
}
|
||||
|
||||
static void dispc_digit_out_enable_isr(void *data, u32 mask)
|
||||
{
|
||||
struct completion *compl = data;
|
||||
|
||||
/* ignore any sync lost interrupts */
|
||||
if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD))
|
||||
complete(compl);
|
||||
}
|
||||
|
||||
static void dispc_mgr_enable_digit_out(void)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(vsync_compl);
|
||||
int r;
|
||||
u32 irq_mask;
|
||||
|
||||
if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Digit output produces some sync lost interrupts during the first
|
||||
* frame when enabling. Those need to be ignored, so we register for the
|
||||
* sync lost irq to prevent the error handler from triggering.
|
||||
*/
|
||||
|
||||
irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) |
|
||||
dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT);
|
||||
|
||||
r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl,
|
||||
irq_mask);
|
||||
if (r) {
|
||||
DSSERR("failed to register %x isr\n", irq_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true);
|
||||
|
||||
/* wait for the first evsync */
|
||||
if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100)))
|
||||
DSSERR("timeout waiting for digit out to start\n");
|
||||
|
||||
r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl,
|
||||
irq_mask);
|
||||
if (r)
|
||||
DSSERR("failed to unregister %x isr\n", irq_mask);
|
||||
}
|
||||
|
||||
static void dispc_mgr_disable_digit_out(void)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(framedone_compl);
|
||||
int r, i;
|
||||
u32 irq_mask;
|
||||
int num_irqs;
|
||||
|
||||
if (!dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
|
||||
return;
|
||||
|
||||
/*
|
||||
* When we disable the digit output, we need to wait for FRAMEDONE to
|
||||
* know that DISPC has finished with the output.
|
||||
*/
|
||||
|
||||
irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT);
|
||||
num_irqs = 1;
|
||||
|
||||
if (!irq_mask) {
|
||||
/*
|
||||
* omap 2/3 don't have framedone irq for TV, so we need to use
|
||||
* vsyncs for this.
|
||||
*/
|
||||
|
||||
irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT);
|
||||
/*
|
||||
* We need to wait for both even and odd vsyncs. Note that this
|
||||
* is not totally reliable, as we could get a vsync interrupt
|
||||
* before we disable the output, which leads to timeout in the
|
||||
* wait_for_completion.
|
||||
*/
|
||||
num_irqs = 2;
|
||||
}
|
||||
|
||||
r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||||
irq_mask);
|
||||
if (r)
|
||||
DSSERR("failed to register %x isr\n", irq_mask);
|
||||
|
||||
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false);
|
||||
|
||||
/* if we couldn't register the irq, just sleep and exit */
|
||||
if (r) {
|
||||
msleep(100);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_irqs; ++i) {
|
||||
if (!wait_for_completion_timeout(&framedone_compl,
|
||||
msecs_to_jiffies(100)))
|
||||
DSSERR("timeout waiting for digit out to stop\n");
|
||||
}
|
||||
|
||||
r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||||
irq_mask);
|
||||
if (r)
|
||||
DSSERR("failed to unregister %x isr\n", irq_mask);
|
||||
}
|
||||
|
||||
void dispc_mgr_enable_sync(enum omap_channel channel)
|
||||
{
|
||||
if (dss_mgr_is_lcd(channel))
|
||||
dispc_mgr_enable_lcd_out(channel);
|
||||
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
|
||||
dispc_mgr_enable_digit_out();
|
||||
else
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
void dispc_mgr_disable_sync(enum omap_channel channel)
|
||||
{
|
||||
if (dss_mgr_is_lcd(channel))
|
||||
dispc_mgr_disable_lcd_out(channel);
|
||||
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
|
||||
dispc_mgr_disable_digit_out();
|
||||
else
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
static inline void dispc_irq_wait_handler(void *data, u32 mask)
|
||||
{
|
||||
complete((struct completion *)data);
|
||||
}
|
||||
|
||||
int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
|
||||
unsigned long timeout)
|
||||
{
|
||||
|
||||
int r;
|
||||
DECLARE_COMPLETION_ONSTACK(completion);
|
||||
|
||||
r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
|
||||
irqmask);
|
||||
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
timeout = wait_for_completion_interruptible_timeout(&completion,
|
||||
timeout);
|
||||
|
||||
omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
|
||||
|
||||
if (timeout == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (timeout == -ERESTARTSYS)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __OMAP2_DSS_DISPC_COMPAT_H
|
||||
#define __OMAP2_DSS_DISPC_COMPAT_H
|
||||
|
||||
void dispc_mgr_enable_sync(enum omap_channel channel);
|
||||
void dispc_mgr_disable_sync(enum omap_channel channel);
|
||||
|
||||
int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
|
||||
unsigned long timeout);
|
||||
|
||||
int dss_dispc_initialize_irq(void);
|
||||
void dss_dispc_uninitialize_irq(void);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,918 @@
|
|||
/*
|
||||
* linux/drivers/video/omap2/dss/dispc.h
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments
|
||||
* Author: Archit Taneja <archit@ti.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __OMAP2_DISPC_REG_H
|
||||
#define __OMAP2_DISPC_REG_H
|
||||
|
||||
/* DISPC common registers */
|
||||
#define DISPC_REVISION 0x0000
|
||||
#define DISPC_SYSCONFIG 0x0010
|
||||
#define DISPC_SYSSTATUS 0x0014
|
||||
#define DISPC_IRQSTATUS 0x0018
|
||||
#define DISPC_IRQENABLE 0x001C
|
||||
#define DISPC_CONTROL 0x0040
|
||||
#define DISPC_CONFIG 0x0044
|
||||
#define DISPC_CAPABLE 0x0048
|
||||
#define DISPC_LINE_STATUS 0x005C
|
||||
#define DISPC_LINE_NUMBER 0x0060
|
||||
#define DISPC_GLOBAL_ALPHA 0x0074
|
||||
#define DISPC_CONTROL2 0x0238
|
||||
#define DISPC_CONFIG2 0x0620
|
||||
#define DISPC_DIVISOR 0x0804
|
||||
#define DISPC_GLOBAL_BUFFER 0x0800
|
||||
#define DISPC_CONTROL3 0x0848
|
||||
#define DISPC_CONFIG3 0x084C
|
||||
#define DISPC_MSTANDBY_CTRL 0x0858
|
||||
#define DISPC_GLOBAL_MFLAG_ATTRIBUTE 0x085C
|
||||
|
||||
/* DISPC overlay registers */
|
||||
#define DISPC_OVL_BA0(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_BA0_OFFSET(n))
|
||||
#define DISPC_OVL_BA1(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_BA1_OFFSET(n))
|
||||
#define DISPC_OVL_BA0_UV(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_BA0_UV_OFFSET(n))
|
||||
#define DISPC_OVL_BA1_UV(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_BA1_UV_OFFSET(n))
|
||||
#define DISPC_OVL_POSITION(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_POS_OFFSET(n))
|
||||
#define DISPC_OVL_SIZE(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_SIZE_OFFSET(n))
|
||||
#define DISPC_OVL_ATTRIBUTES(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ATTR_OFFSET(n))
|
||||
#define DISPC_OVL_ATTRIBUTES2(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ATTR2_OFFSET(n))
|
||||
#define DISPC_OVL_FIFO_THRESHOLD(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIFO_THRESH_OFFSET(n))
|
||||
#define DISPC_OVL_FIFO_SIZE_STATUS(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIFO_SIZE_STATUS_OFFSET(n))
|
||||
#define DISPC_OVL_ROW_INC(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ROW_INC_OFFSET(n))
|
||||
#define DISPC_OVL_PIXEL_INC(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_PIX_INC_OFFSET(n))
|
||||
#define DISPC_OVL_WINDOW_SKIP(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_WINDOW_SKIP_OFFSET(n))
|
||||
#define DISPC_OVL_TABLE_BA(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_TABLE_BA_OFFSET(n))
|
||||
#define DISPC_OVL_FIR(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_OFFSET(n))
|
||||
#define DISPC_OVL_FIR2(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR2_OFFSET(n))
|
||||
#define DISPC_OVL_PICTURE_SIZE(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_PIC_SIZE_OFFSET(n))
|
||||
#define DISPC_OVL_ACCU0(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ACCU0_OFFSET(n))
|
||||
#define DISPC_OVL_ACCU1(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ACCU1_OFFSET(n))
|
||||
#define DISPC_OVL_ACCU2_0(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ACCU2_0_OFFSET(n))
|
||||
#define DISPC_OVL_ACCU2_1(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ACCU2_1_OFFSET(n))
|
||||
#define DISPC_OVL_FIR_COEF_H(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_H_OFFSET(n, i))
|
||||
#define DISPC_OVL_FIR_COEF_HV(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_HV_OFFSET(n, i))
|
||||
#define DISPC_OVL_FIR_COEF_H2(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_H2_OFFSET(n, i))
|
||||
#define DISPC_OVL_FIR_COEF_HV2(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_HV2_OFFSET(n, i))
|
||||
#define DISPC_OVL_CONV_COEF(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_CONV_COEF_OFFSET(n, i))
|
||||
#define DISPC_OVL_FIR_COEF_V(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_V_OFFSET(n, i))
|
||||
#define DISPC_OVL_FIR_COEF_V2(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_V2_OFFSET(n, i))
|
||||
#define DISPC_OVL_PRELOAD(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_PRELOAD_OFFSET(n))
|
||||
#define DISPC_OVL_MFLAG_THRESHOLD(n) DISPC_MFLAG_THRESHOLD_OFFSET(n)
|
||||
|
||||
/* DISPC up/downsampling FIR filter coefficient structure */
|
||||
struct dispc_coef {
|
||||
s8 hc4_vc22;
|
||||
s8 hc3_vc2;
|
||||
u8 hc2_vc1;
|
||||
s8 hc1_vc0;
|
||||
s8 hc0_vc00;
|
||||
};
|
||||
|
||||
const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps);
|
||||
|
||||
/* DISPC manager/channel specific registers */
|
||||
static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x004C;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
return 0x0050;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03AC;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0814;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0054;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
return 0x0058;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03B0;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0818;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_TIMING_H(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0064;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x0400;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0840;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_TIMING_V(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0068;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x0404;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0844;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_POL_FREQ(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x006C;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x0408;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x083C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_DIVISORo(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0070;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x040C;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0838;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Named as DISPC_SIZE_LCD, DISPC_SIZE_DIGIT and DISPC_SIZE_LCD2 in TRM */
|
||||
static inline u16 DISPC_SIZE_MGR(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x007C;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
return 0x0078;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03CC;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0834;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x01D4;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03C0;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0828;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x01D8;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03C4;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x082C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x01DC;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03C8;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0830;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0220;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03BC;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0824;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0224;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03B8;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0820;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0228;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03B4;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x081C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* DISPC overlay register base addresses */
|
||||
static inline u16 DISPC_OVL_BASE(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0080;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x00BC;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x014C;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0300;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0500;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* DISPC overlay register offsets */
|
||||
static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0000;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0008;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0004;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x000C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0544;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x04BC;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0310;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0118;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0548;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x04C0;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0314;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x011C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_POS_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0008;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x009C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x000C;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x00A8;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0020;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0010;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0070;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0568;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x04DC;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x032C;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0310;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0024;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0014;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x008C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0028;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0018;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0088;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x002C;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x001C;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x00A4;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0030;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0020;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0098;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0034;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
case OMAP_DSS_VIDEO3:
|
||||
BUG();
|
||||
return 0;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0038;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
case OMAP_DSS_VIDEO3:
|
||||
BUG();
|
||||
return 0;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0024;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0090;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0580;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x055C;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0424;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x290;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0028;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0094;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x002C;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0000;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0584;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0560;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0428;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0294;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0030;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0004;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0588;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0564;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x042C;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0298;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0034 + i * 0x8;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0010 + i * 0x8;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x058C + i * 0x8;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0568 + i * 0x8;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0430 + i * 0x8;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x02A0 + i * 0x8;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0038 + i * 0x8;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0014 + i * 0x8;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0590 + i * 8;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x056C + i * 0x8;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0434 + i * 0x8;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x02A4 + i * 0x8;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4,} */
|
||||
static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0074 + i * 0x4;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0124 + i * 0x4;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x00B4 + i * 0x4;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0050 + i * 0x4;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x05CC + i * 0x4;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x05A8 + i * 0x4;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0470 + i * 0x4;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x02E0 + i * 0x4;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x01AC;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0174;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x00E8;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x00A0;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_MFLAG_THRESHOLD_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0860;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0864;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0868;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x086c;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0870;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue