summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/video/mxc/mxc_elcdif_fb.c134
-rw-r--r--include/linux/mxcfb.h8
2 files changed, 138 insertions, 4 deletions
diff --git a/drivers/video/mxc/mxc_elcdif_fb.c b/drivers/video/mxc/mxc_elcdif_fb.c
index d127ff4ca7d6..f34d3ac599f5 100644
--- a/drivers/video/mxc/mxc_elcdif_fb.c
+++ b/drivers/video/mxc/mxc_elcdif_fb.c
@@ -86,6 +86,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);
@@ -97,6 +103,15 @@ 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 struct mxcfb_mode mxc_disp_mode;
+
+static void dump_fb_videomode(struct fb_videomode *m)
+{
+ pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ m->refresh, m->xres, m->yres, m->pixclock, m->left_margin,
+ m->right_margin, m->upper_margin, m->lower_margin,
+ m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
+}
static inline void setup_dotclk_panel(u32 pixel_clk,
u16 v_pulse_width,
@@ -113,16 +128,39 @@ static inline void setup_dotclk_panel(u32 pixel_clk,
int enable_present)
{
u32 val, rounded_pixel_clk;
+ u32 rounded_parent_clk;
+ struct clk *clk_parent;
if (!g_elcdif_axi_clk_enable) {
clk_enable(g_elcdif_axi_clk);
g_elcdif_axi_clk_enable = true;
}
+ /* Init clocking */
dev_dbg(g_elcdif_dev, "pixel clk = %d\n", pixel_clk);
- rounded_pixel_clk = clk_round_rate(g_elcdif_pix_clk, pixel_clk);
+
+ clk_parent = clk_get_parent(g_elcdif_pix_clk);
+ if (clk_parent == NULL)
+ dev_err(g_elcdif_dev, "%s Failed get clk parent\n", __func__);
+
+ rounded_pixel_clk = pixel_clk * 2;
+
+ rounded_parent_clk = clk_round_rate(clk_parent,
+ rounded_pixel_clk);
+
+ while (rounded_pixel_clk < rounded_parent_clk) {
+ /* the max divider from parent to di is 8 */
+ if (rounded_parent_clk / pixel_clk < 8)
+ rounded_pixel_clk += pixel_clk * 2;
+ }
+
+ clk_set_rate(clk_parent, rounded_pixel_clk);
+ rounded_pixel_clk =
+ clk_round_rate(g_elcdif_pix_clk, pixel_clk);
clk_set_rate(g_elcdif_pix_clk, rounded_pixel_clk);
+ msleep(5);
+
__raw_writel(BM_ELCDIF_CTRL_DATA_SHIFT_DIR,
elcdif_base + HW_ELCDIF_CTRL_CLR);
@@ -519,6 +557,35 @@ 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;
+
+ mode = kzalloc(num_modes * sizeof(struct fb_videomode), GFP_KERNEL);
+
+ if (!mode) {
+ dev_err(g_elcdif_dev, "%s Failed to allocate mode data\n", __func__);
+ return;
+ }
+
+ 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;
@@ -1234,6 +1301,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) {
@@ -1294,20 +1364,63 @@ 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 {
+ dev_dbg(&pdev->dev, "Added mode %d:", i);
+ dev_dbg(&pdev->dev,
+ "xres = %d, yres = %d, freq = %d, vmode = %d, flag = %d\n",
+ mode[i].xres, mode[i].yres, mode[i].refresh, mode[i].vmode,
+ mode[i].flag);
+ fb_add_videomode(&mode[i], &fbi->modelist);
+ }
+ }
+ }
+
if (!fb_mode && pdata && pdata->mode_str)
fb_mode = pdata->mode_str;
if (fb_mode) {
+ dev_dbg(&pdev->dev, "default video mode %s\n", 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);
+ dump_fb_videomode(&m);
+ 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);
@@ -1341,6 +1454,19 @@ static int mxc_elcdif_fb_probe(struct platform_device *pdev)
*/
clk_set_rate(g_elcdif_pix_clk, 25000000);
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ console_lock();
+ fbi->flags |= FBINFO_MISC_USEREVENT;
+ ret = fb_set_var(fbi, &fbi->var);
+ fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+
+ if (data->cur_blank == FB_BLANK_UNBLANK) {
+ console_lock();
+ fb_blank(fbi, FB_BLANK_UNBLANK);
+ console_unlock();
+ }
+
ret = register_framebuffer(fbi);
if (ret)
goto err3;
diff --git a/include/linux/mxcfb.h b/include/linux/mxcfb.h
index e852a8af7369..be391e0b7e05 100644
--- a/include/linux/mxcfb.h
+++ b/include/linux/mxcfb.h
@@ -162,6 +162,11 @@ extern struct fb_videomode mxcfb_modedb[];
extern int mxcfb_modedb_sz;
enum {
+ MXC_DISP_SPEC_DEV = 0,
+ MXC_DISP_DDC_DEV = 1,
+};
+
+enum {
MXCFB_REFRESH_OFF,
MXCFB_REFRESH_AUTO,
MXCFB_REFRESH_PARTIAL,
@@ -170,5 +175,8 @@ enum {
int mxcfb_set_refresh_mode(struct fb_info *fbi, int mode,
struct mxcfb_rect *update_region);
int mxc_elcdif_frame_addr_setup(dma_addr_t phys);
+void mxcfb_elcdif_register_mode(const struct fb_videomode *modedb,
+ int num_modes, int dev_mode);
+
#endif /* __KERNEL__ */
#endif