*/
#define DEBUG_VAR 1
+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
+ (defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) && \
+ defined(CONFIG_FB_IMX_MODULE))
+#define PWMR_BACKLIGHT_AVAILABLE
+#endif
+
#define DRIVER_NAME "imx-fb"
#define LCDC_SSA 0x00
#define LCDC_SIZE 0x04
#define SIZE_XMAX(x) ((((x) >> 4) & 0x3f) << 20)
-#ifdef CONFIG_ARCH_MX1
-#define SIZE_YMAX(y) ((y) & 0x1ff)
-#else
-#define SIZE_YMAX(y) ((y) & 0x3ff)
-#endif
+#define YMAX_MASK (cpu_is_mx1() ? 0x1ff : 0x3ff)
+#define SIZE_YMAX(y) ((y) & YMAX_MASK)
#define LCDC_VPW 0x08
#define VPW_VPW(x) ((x) & 0x3ff)
#define CPOS_OP (1<<28)
#define CPOS_CXP(x) (((x) & 3ff) << 16)
-#ifdef CONFIG_ARCH_MX1
-#define CPOS_CYP(y) ((y) & 0x1ff)
-#else
-#define CPOS_CYP(y) ((y) & 0x3ff)
-#endif
-
#define LCDC_LCWHB 0x10
#define LCWHB_BK_EN (1<<31)
#define LCWHB_CW(w) (((w) & 0x1f) << 24)
#define LCDC_LCHCC 0x14
-#ifdef CONFIG_ARCH_MX1
-#define LCHCC_CUR_COL_R(r) (((r) & 0x1f) << 11)
-#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 5)
-#define LCHCC_CUR_COL_B(b) ((b) & 0x1f)
-#else
-#define LCHCC_CUR_COL_R(r) (((r) & 0x3f) << 12)
-#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 6)
-#define LCHCC_CUR_COL_B(b) ((b) & 0x3f)
-#endif
-
#define LCDC_PCR 0x18
#define LCDC_HCR 0x1C
#define LCDC_RMCR 0x34
-#ifdef CONFIG_ARCH_MX1
-#define RMCR_LCDC_EN (1<<1)
-#else
-#define RMCR_LCDC_EN 0
-#endif
+#define RMCR_LCDC_EN_MX1 (1<<1)
#define RMCR_SELF_REF (1<<0)
struct imx_fb_videomode *mode;
int num_modes;
+#ifdef PWMR_BACKLIGHT_AVAILABLE
+ struct backlight_device *bl;
+#endif
void (*lcd_power)(int);
void (*backlight_power)(int);
return 0;
}
+#ifdef PWMR_BACKLIGHT_AVAILABLE
+static int imxfb_bl_get_brightness(struct backlight_device *bl)
+{
+ struct imxfb_info *fbi = bl_get_data(bl);
+
+ return readl(fbi->regs + LCDC_PWMR) & 0xFF;
+}
+
+static int imxfb_bl_update_status(struct backlight_device *bl)
+{
+ struct imxfb_info *fbi = bl_get_data(bl);
+ int brightness = bl->props.brightness;
+
+ if (bl->props.power != FB_BLANK_UNBLANK)
+ brightness = 0;
+ if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+ brightness = 0;
+
+ fbi->pwmr = (fbi->pwmr & ~0xFF) | brightness;
+
+ if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+ clk_enable(fbi->clk);
+ writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+ if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+ clk_disable(fbi->clk);
+
+ return 0;
+}
+
+static const struct backlight_ops imxfb_lcdc_bl_ops = {
+ .update_status = imxfb_bl_update_status,
+ .get_brightness = imxfb_bl_get_brightness,
+};
+
+static void imxfb_init_backlight(struct imxfb_info *fbi)
+{
+ struct backlight_properties props;
+ struct backlight_device *bl;
+
+ if (fbi->bl)
+ return;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.max_brightness = 0xff;
+ props.type = BACKLIGHT_RAW;
+ writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+
+ bl = backlight_device_register("imxfb-bl", &fbi->pdev->dev, fbi,
+ &imxfb_lcdc_bl_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&fbi->pdev->dev, "error %ld on backlight register\n",
+ PTR_ERR(bl));
+ return;
+ }
+
+ fbi->bl = bl;
+ bl->props.power = FB_BLANK_UNBLANK;
+ bl->props.fb_blank = FB_BLANK_UNBLANK;
+ bl->props.brightness = imxfb_bl_get_brightness(bl);
+}
+
+static void imxfb_exit_backlight(struct imxfb_info *fbi)
+{
+ if (fbi->bl)
+ backlight_device_unregister(fbi->bl);
+}
+#endif
+
static void imxfb_enable_controller(struct imxfb_info *fbi)
{
pr_debug("Enabling LCD controller\n");
writel(readl(fbi->regs + LCDC_CPOS) & ~(CPOS_CC0 | CPOS_CC1),
fbi->regs + LCDC_CPOS);
- writel(RMCR_LCDC_EN, fbi->regs + LCDC_RMCR);
+ /*
+ * RMCR_LCDC_EN_MX1 is present on i.MX1 only, but doesn't hurt
+ * on other SoCs
+ */
+ writel(RMCR_LCDC_EN_MX1, fbi->regs + LCDC_RMCR);
clk_enable(fbi->clk);
if (var->right_margin > 255)
printk(KERN_ERR "%s: invalid right_margin %d\n",
info->fix.id, var->right_margin);
- if (var->yres < 1 || var->yres > 511)
+ if (var->yres < 1 || var->yres > YMAX_MASK)
printk(KERN_ERR "%s: invalid yres %d\n",
info->fix.id, var->yres);
if (var->vsync_len > 100)
fbi->regs + LCDC_SIZE);
writel(fbi->pcr, fbi->regs + LCDC_PCR);
+#ifndef PWMR_BACKLIGHT_AVAILABLE
writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+#endif
writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
*/
static int imxfb_suspend(struct platform_device *dev, pm_message_t state)
{
- struct imxfb_info *fbi = platform_get_drvdata(dev);
+ struct fb_info *info = platform_get_drvdata(dev);
+ struct imxfb_info *fbi = info->par;
pr_debug("%s\n", __func__);
static int imxfb_resume(struct platform_device *dev)
{
- struct imxfb_info *fbi = platform_get_drvdata(dev);
+ struct fb_info *info = platform_get_drvdata(dev);
+ struct imxfb_info *fbi = info->par;
pr_debug("%s\n", __func__);
}
imxfb_enable_controller(fbi);
+ fbi->pdev = pdev;
+#ifdef PWMR_BACKLIGHT_AVAILABLE
+ imxfb_init_backlight(fbi);
+#endif
return 0;
dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,
fbi->map_dma);
failed_map:
- clk_put(fbi->clk);
-failed_getclock:
iounmap(fbi->regs);
failed_ioremap:
+ clk_put(fbi->clk);
+failed_getclock:
release_mem_region(res->start, resource_size(res));
failed_req:
kfree(info->pseudo_palette);
imxfb_disable_controller(fbi);
+#ifdef PWMR_BACKLIGHT_AVAILABLE
+ imxfb_exit_backlight(fbi);
+#endif
unregister_framebuffer(info);
pdata = pdev->dev.platform_data;
module_init(imxfb_init);
module_exit(imxfb_cleanup);
-MODULE_DESCRIPTION("Motorola i.MX framebuffer driver");
+MODULE_DESCRIPTION("Freescale i.MX framebuffer driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");