summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
authorDanny Nold <dannynold@freescale.com>2011-04-07 20:59:57 -0500
committerAlex Gonzalez <alex.gonzalez@digi.com>2011-08-01 09:51:32 +0200
commit1d747de353dc15890c3153ba0290552dfa7cdc0a (patch)
tree60ef55feef4bac0192ba64aa1520705bf65254a8 /drivers/video
parent14e876ae11fd437855afde7714b5721d4394ab95 (diff)
ENGR00140983-4 - SII902X fb: Support HDMI driven through LCDIF module
- Defined new API to allow client display FB drivers to pass videomode information to the LCDIF. - SII902X added calls to enable/disable pins through platform-level function pointers. - Changed SII902X driver to ensure that HDMI detect routine gets called once FB is registered, if a hotplug interrupt has previously been detected. - Added call to display boot logo once FB is registered. - Modified LCDIF to incorporate videomodes passed in from client display FB drivers. Signed-off-by: Danny Nold <dannynold@freescale.com> (cherry picked from commit b78d67317ca2775920d8a988c243d51ffac00f22) Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/mxc/mxc_elcdif_fb.c85
-rw-r--r--drivers/video/mxc/mxcfb_sii902x.c68
2 files changed, 123 insertions, 30 deletions
diff --git a/drivers/video/mxc/mxc_elcdif_fb.c b/drivers/video/mxc/mxc_elcdif_fb.c
index 90dfe55ece42..d9708f1ef8ce 100644
--- a/drivers/video/mxc/mxc_elcdif_fb.c
+++ b/drivers/video/mxc/mxc_elcdif_fb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
*/
/*
@@ -85,6 +85,12 @@ struct elcdif_signal_cfg {
unsigned Vsync_pol:1; /* true = active high */
};
+struct mxcfb_mode {
+ int dev_mode;
+ int num_modes;
+ struct fb_videomode *mode;
+};
+
static int mxc_elcdif_fb_blank(int blank, struct fb_info *info);
static int mxc_elcdif_fb_map_video_memory(struct fb_info *info);
static int mxc_elcdif_fb_unmap_video_memory(struct fb_info *info);
@@ -96,6 +102,7 @@ static bool g_elcdif_axi_clk_enable;
static bool g_elcdif_pix_clk_enable;
static struct clk *g_elcdif_axi_clk;
static struct clk *g_elcdif_pix_clk;
+static __initdata struct mxcfb_mode mxc_disp_mode;
static inline void setup_dotclk_panel(u32 pixel_clk,
u16 v_pulse_width,
@@ -516,6 +523,31 @@ static inline void mxc_init_elcdif(void)
return;
}
+void mxcfb_elcdif_register_mode(const struct fb_videomode *modedb,
+ int num_modes, int dev_mode)
+{
+ struct fb_videomode *mode;
+ int mode_sum;
+
+ mode = kzalloc(num_modes * sizeof(struct fb_videomode), GFP_KERNEL);
+
+ if (mxc_disp_mode.num_modes)
+ memcpy(mode, mxc_disp_mode.mode,
+ mxc_disp_mode.num_modes * sizeof(struct fb_videomode));
+ if (modedb)
+ memcpy(mode + mxc_disp_mode.num_modes, modedb,
+ num_modes * sizeof(struct fb_videomode));
+
+ if (mxc_disp_mode.num_modes)
+ kfree(mxc_disp_mode.mode);
+
+ mxc_disp_mode.mode = mode;
+ mxc_disp_mode.num_modes += num_modes;
+ mxc_disp_mode.dev_mode = dev_mode;
+
+ return;
+}
+
int mxc_elcdif_frame_addr_setup(dma_addr_t phys)
{
int ret = 0;
@@ -1200,6 +1232,9 @@ static int mxc_elcdif_fb_probe(struct platform_device *pdev)
struct resource *res;
struct fb_info *fbi;
struct mxc_fb_platform_data *pdata = pdev->dev.platform_data;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+ int num;
fbi = framebuffer_alloc(sizeof(struct mxc_elcdif_fb_data), &pdev->dev);
if (fbi == NULL) {
@@ -1260,20 +1295,55 @@ static int mxc_elcdif_fb_probe(struct platform_device *pdev)
if (pdata && !data->output_pix_fmt)
data->output_pix_fmt = pdata->interface_pix_fmt;
+ INIT_LIST_HEAD(&fbi->modelist);
+
if (pdata && pdata->mode && pdata->num_modes)
fb_videomode_to_modelist(pdata->mode, pdata->num_modes,
&fbi->modelist);
+ if (mxc_disp_mode.num_modes) {
+ int i;
+ mode = mxc_disp_mode.mode;
+ num = mxc_disp_mode.num_modes;
+
+ for (i = 0; i < num; i++) {
+ /*
+ * FIXME now we do not support interlaced
+ * mode for ddc mode
+ */
+ if ((mxc_disp_mode.dev_mode
+ & MXC_DISP_DDC_DEV) &&
+ (mode[i].vmode & FB_VMODE_INTERLACED))
+ continue;
+ else
+ fb_add_videomode(&mode[i], &fbi->modelist);
+ }
+ }
+
if (!fb_mode && pdata && pdata->mode_str)
fb_mode = pdata->mode_str;
if (fb_mode) {
ret = fb_find_mode(&fbi->var, fbi, fb_mode, NULL, 0, NULL,
default_bpp);
- if ((!ret || (ret > 2)) && pdata && pdata->mode &&
- pdata->num_modes)
- fb_find_mode(&fbi->var, fbi, fb_mode, pdata->mode,
+ if ((ret == 1) || (ret == 2)) {
+ fb_var_to_videomode(&m, &fbi->var);
+ mode = fb_find_nearest_mode(&m,
+ &fbi->modelist);
+ fb_videomode_to_var(&fbi->var, mode);
+ } else if (pdata && pdata->mode && pdata->num_modes) {
+ ret = fb_find_mode(&fbi->var, fbi, fb_mode, pdata->mode,
pdata->num_modes, NULL, default_bpp);
+ if (!ret) {
+ dev_err(fbi->device,
+ "No valid video mode found");
+ goto err2;
+ }
+ } else {
+ dev_err(fbi->device,
+ "No valid video mode found");
+ goto err2;
+ }
}
mxc_elcdif_fb_check_var(&fbi->var, fbi);
@@ -1307,6 +1377,13 @@ static int mxc_elcdif_fb_probe(struct platform_device *pdev)
*/
clk_set_rate(g_elcdif_pix_clk, 25000000);
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ acquire_console_sem();
+ fbi->flags |= FBINFO_MISC_USEREVENT;
+ ret = fb_set_var(fbi, &fbi->var);
+ fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ release_console_sem();
+
ret = register_framebuffer(fbi);
if (ret)
goto err3;
diff --git a/drivers/video/mxc/mxcfb_sii902x.c b/drivers/video/mxc/mxcfb_sii902x.c
index a1c748e63cfd..c0dd5c893e69 100644
--- a/drivers/video/mxc/mxcfb_sii902x.c
+++ b/drivers/video/mxc/mxcfb_sii902x.c
@@ -59,13 +59,12 @@ static int g_enable_hdmi;
struct sii902x_data {
struct platform_device *pdev;
struct i2c_client *client;
- struct regulator *io_reg;
- struct regulator *analog_reg;
struct delayed_work det_work;
struct fb_info *fbi;
struct mxc_edid_cfg edid_cfg;
u8 cable_plugin;
u8 edid[SII_EDID_LEN];
+ bool waiting_for_fb;
} sii902x;
static void sii902x_poweron(void);
@@ -279,6 +278,9 @@ static irqreturn_t sii902x_detect_handler(int irq, void *data)
{
if (sii902x.fbi)
schedule_delayed_work(&(sii902x.det_work), msecs_to_jiffies(20));
+ else
+ sii902x.waiting_for_fb = true;
+
return IRQ_HANDLED;
}
@@ -287,20 +289,14 @@ static int sii902x_fb_event(struct notifier_block *nb, unsigned long val, void *
struct fb_event *event = v;
struct fb_info *fbi = event->info;
- /* assume sii902x on DI0 only */
- if ((IPU_DISP_PORT)) {
- if (strcmp(event->info->fix.id, "DISP3 BG - DI1"))
- return 0;
- } else {
- if (strcmp(event->info->fix.id, "DISP3 BG"))
- return 0;
- }
-
switch (val) {
case FB_EVENT_FB_REGISTERED:
- if (sii902x.fbi != NULL)
- break;
- sii902x.fbi = fbi;
+ if (sii902x.fbi == NULL) {
+ sii902x.fbi = fbi;
+ if (sii902x.waiting_for_fb)
+ det_worker(NULL);
+ }
+ fb_show_logo(fbi, 0);
break;
case FB_EVENT_MODE_CHANGE:
sii902x_setup(fbi);
@@ -339,16 +335,10 @@ static int __devinit sii902x_probe(struct i2c_client *client,
sii902x.client = client;
- sii902x.io_reg = regulator_get(&sii902x.client->dev, plat->io_reg);
- if (!IS_ERR(sii902x.io_reg)) {
- regulator_set_voltage(sii902x.io_reg, 3300000, 3300000);
- regulator_enable(sii902x.io_reg);
- }
- sii902x.analog_reg = regulator_get(&sii902x.client->dev, plat->analog_reg);
- if (!IS_ERR(sii902x.analog_reg)) {
- regulator_set_voltage(sii902x.analog_reg, 1300000, 1300000);
- regulator_enable(sii902x.analog_reg);
- }
+ /* Claim HDMI pins */
+ if (plat->get_pins)
+ if (!plat->get_pins())
+ return -EACCES;
if (plat->reset) {
sii902x_reset = plat->reset;
@@ -383,13 +373,20 @@ static int __devinit sii902x_probe(struct i2c_client *client,
}
/* try to read edid */
- if (sii902x_read_edid(&edid_fbi) < 0)
+ ret = sii902x_read_edid(&edid_fbi);
+ if (ret < 0)
dev_warn(&sii902x.client->dev, "Can not read edid\n");
+
#if defined(CONFIG_MXC_IPU_V3) && defined(CONFIG_FB_MXC_SYNC_PANEL)
- else
+ if (ret >= 0)
mxcfb_register_mode(IPU_DISP_PORT, edid_fbi.monspecs.modedb,
edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV);
#endif
+#if defined(CONFIG_FB_MXC_ELCDIF_FB)
+ if (ret >= 0)
+ mxcfb_elcdif_register_mode(edid_fbi.monspecs.modedb,
+ edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV);
+#endif
if (sii902x.client->irq) {
ret = request_irq(sii902x.client->irq, sii902x_detect_handler,
@@ -425,8 +422,15 @@ static int __devinit sii902x_probe(struct i2c_client *client,
static int __devexit sii902x_remove(struct i2c_client *client)
{
+ struct mxc_lcd_platform_data *plat = sii902x.client->dev.platform_data;
+
fb_unregister_client(&nb);
sii902x_poweroff();
+
+ /* Release HDMI pins */
+ if (plat->put_pins)
+ plat->put_pins();
+
return 0;
}
@@ -444,6 +448,12 @@ static int sii902x_resume(struct i2c_client *client)
static void sii902x_poweron(void)
{
+ struct mxc_lcd_platform_data *plat = sii902x.client->dev.platform_data;
+
+ /* Enable pins to HDMI */
+ if (plat->enable_pins)
+ plat->enable_pins();
+
/* Turn on DVI or HDMI */
if (sii902x.edid_cfg.hdmi_cap)
i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x01);
@@ -454,12 +464,18 @@ static void sii902x_poweron(void)
static void sii902x_poweroff(void)
{
+ struct mxc_lcd_platform_data *plat = sii902x.client->dev.platform_data;
+
/* disable tmds before changing resolution */
if (sii902x.edid_cfg.hdmi_cap)
i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x11);
else
i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x10);
+ /* Disable pins to HDMI */
+ if (plat->disable_pins)
+ plat->disable_pins();
+
return;
}