/*! * @brief Config and request LCDC interrupt */ static void _request_irq(void) { unsigned long status; unsigned long flags; /* Read to clear the status */ status = __raw_readl(LCDC_REG(LCDC_LISR)); if (request_irq(MXC_INT_LCDC, mx2fb_isr, 0, "LCDC", 0)) pr_info("Request LCDC IRQ failed.\n"); else { spin_lock_irqsave(&mx2fb_notifier_list.lock, flags); /* Enable interrupt in case client has registered */ if (mx2fb_notifier_list.head != NULL) { unsigned long status; unsigned long ints = MX2FB_INT_EOF; ints |= MX2FB_INT_GW_EOF; /* Read to clear the status */ status = __raw_readl(LCDC_REG(LCDC_LISR)); /* Configure interrupt condition for EOF */ __raw_writel(0x0, LCDC_REG(LCDC_LICR)); /* Enable EOF and graphic window EOF interrupt */ __raw_writel(ints, LCDC_REG(LCDC_LIER)); } spin_unlock_irqrestore(&mx2fb_notifier_list.lock, flags); } }
/*! * @brief Register a client notifier * @param nb notifier block to callback on events */ int mx2fb_register_client(struct notifier_block *nb) { unsigned long flags; int ret; ret = atomic_notifier_chain_register(&mx2fb_notifier_list, nb); spin_lock_irqsave(&mx2fb_notifier_list.lock, flags); /* Enable interrupt in case client has registered */ if (mx2fb_notifier_list.head != NULL) { unsigned long status; unsigned long ints = MX2FB_INT_EOF; ints |= MX2FB_INT_GW_EOF; /* Read to clear the status */ status = __raw_readl(LCDC_REG(LCDC_LISR)); /* Configure interrupt condition for EOF */ __raw_writel(0x0, LCDC_REG(LCDC_LICR)); /* Enable EOF and graphic window EOF interrupt */ __raw_writel(ints, LCDC_REG(LCDC_LIER)); } spin_unlock_irqrestore(&mx2fb_notifier_list.lock, flags); return ret; }
/*! * @brief Free LCDC interrupt handler */ static void _free_irq(void) { /* Disable all LCDC interrupt */ __raw_writel(0x0, LCDC_REG(LCDC_LIER)); free_irq(MXC_INT_LCDC, 0); }
/*! * @brief Disable graphic window. * @param info framebuffer information pointer */ static void _disable_graphic_window(struct fb_info *info) { unsigned long i = 0; g_gwinfo.enabled = 0; /* * Set alpha value to zero and reduce gw size, otherwise the graphic * window will not be able to be enabled again. */ __raw_writel(__raw_readl(LCDC_REG(LCDC_LGWCR)) & 0x00FFFFFF, LCDC_REG(LCDC_LGWCR)); __raw_writel(((16 >> 4) << 20) + 16, LCDC_REG(LCDC_LGWSR)); while (i < 1000) i++; /* Now disable graphic window */ __raw_writel(__raw_readl(LCDC_REG(LCDC_LGWCR)) & ~0x00400000, LCDC_REG(LCDC_LGWCR)); dev_dbg(info->device, "Graphic window disabled.\n"); }
/*! * @brief Unregister a client notifier * @param nb notifier block to callback on events */ int mx2fb_unregister_client(struct notifier_block *nb) { unsigned long flags; int ret; ret = atomic_notifier_chain_unregister(&mx2fb_notifier_list, nb); spin_lock_irqsave(&mx2fb_notifier_list.lock, flags); /* Mask interrupt in case no client registered */ if (mx2fb_notifier_list.head == NULL) __raw_writel(0x0, LCDC_REG(LCDC_LIER)); spin_unlock_irqrestore(&mx2fb_notifier_list.lock, flags); return ret; }
/* * @brief LCDC interrupt handler */ static irqreturn_t mx2fb_isr(int irq, void *dev_id) { struct fb_event event; unsigned long status = __raw_readl(LCDC_REG(LCDC_LISR)); if (status & MX2FB_INT_EOF) { event.info = &mx2fb_info[0]; atomic_notifier_call_chain(&mx2fb_notifier_list, FB_EVENT_MXC_EOF, &event); } if (status & MX2FB_INT_GW_EOF) { event.info = &mx2fb_info[1]; atomic_notifier_call_chain(&mx2fb_notifier_list, FB_EVENT_MXC_EOF, &event); } return IRQ_HANDLED; }
/*! * @brief Setup graphic window properties. * @param gwinfo graphic window information pointer */ void mx2_gw_set(struct fb_gwinfo *gwinfo) { int width, height, xpos, ypos; int width_bg, height_bg; unsigned long lgwcr = 0x00400000; /* Graphic window control register */ if (!gwinfo->enabled) { _disable_graphic_window(0); return; } /* Graphic window start address register */ __raw_writel(gwinfo->base, LCDC_REG(LCDC_LGWSAR)); /* * The graphic window width, height, x position and y position * must be synced up width the background window, otherwise there * may be flickering. */ width_bg = (__raw_readl(LCDC_REG(LCDC_LSR)) & 0x03F00000) >> 16; height_bg = __raw_readl(LCDC_REG(LCDC_LSR)) & 0x000003FF; width = (gwinfo->xres > width_bg) ? width_bg : gwinfo->xres; height = (gwinfo->yres > height_bg) ? height_bg : gwinfo->yres; xpos = gwinfo->xpos; ypos = gwinfo->ypos; if (xpos + width > width_bg) xpos = width_bg - width; if (ypos + height > height_bg) ypos = height_bg - height; /* Graphic window size register */ __raw_writel(((width >> 4) << 20) + height, LCDC_REG(LCDC_LGWSR)); /* Graphic window virtual page width register */ __raw_writel(gwinfo->xres_virtual >> 1, LCDC_REG(LCDC_LGWVPWR)); /* Graphic window position register */ __raw_writel(((xpos & 0x000003FF) << 16) | (ypos & 0x000003FF), LCDC_REG(LCDC_LGWPR)); /* Graphic window panning offset register */ __raw_writel(0, LCDC_REG(LCDC_LGWPOR)); /* Graphic window DMA control register */ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) __raw_writel(0x00040060, LCDC_REG(LCDC_LGWDCR)); else __raw_writel(0x00020010, LCDC_REG(LCDC_LGWDCR)); /* Graphic window control register */ lgwcr |= (gwinfo->alpha_value & 0x000000FF) << 24; lgwcr |= gwinfo->ck_enabled ? 0x00800000 : 0; lgwcr |= gwinfo->vs_reversed ? 0x00200000 : 0; /* * Color keying value * Todo: assume always use RGB565 */ lgwcr |= (gwinfo->ck_red & 0x0000003F) << 12; lgwcr |= (gwinfo->ck_green & 0x0000003F) << 6; lgwcr |= gwinfo->ck_blue & 0x0000003F; __raw_writel(lgwcr, LCDC_REG(LCDC_LGWCR)); pr_debug("Graphic window enabled.\n"); }
/*! * @brief Set LCD brightness * @param level brightness level */ void mx2fb_set_brightness(uint8_t level) { /* Set LCDC PWM contract control register */ __raw_writel(0x00A90300 | level, LCDC_REG(LCDC_LPCCR)); }
/*! * @brief Update LCDC registers * @param info framebuffer information pointer */ static void _update_lcdc(struct fb_info *info) { unsigned long base; unsigned long perclk3, pcd, pcr; struct fb_var_screeninfo *var = &info->var; struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par; if (mx2fbi->type == MX2FB_TYPE_GW) { _enable_graphic_window(info); return; } base = (var->yoffset * var->xres_virtual + var->xoffset); base *= (var->bits_per_pixel) / 8; base += info->fix.smem_start; /* Screen start address register */ __raw_writel(base, LCDC_REG(LCDC_LSSAR)); /* Size register */ dev_dbg(info->device, "xres = %d, yres = %d\n", info->var.xres, info->var.yres); __raw_writel(((info->var.xres >> 4) << 20) + info->var.yres, LCDC_REG(LCDC_LSR)); /* Virtual page width register */ __raw_writel(info->var.xres_virtual >> 1, LCDC_REG(LCDC_LVPWR)); /* To setup LCDC pixel clock */ perclk3 = clk_round_rate(lcdc_clk, 134000000); if (clk_set_rate(lcdc_clk, perclk3)) { printk(KERN_INFO "mx2fb: Unable to set clock to %lu\n", perclk3); perclk3 = clk_get_rate(lcdc_clk); } /* Calculate pixel clock divider, and round to the nearest integer */ pcd = (perclk3 * 8 / (PICOS2KHZ(var->pixclock) * 1000UL) + 4) / 8; if (--pcd > 0x3F) pcd = 0x3F; /* Panel configuration register */ pcr = 0xFA008B80 | pcd; pcr |= (var->sync & FB_SYNC_CLK_INVERT) ? 0x01000000 : 0; pcr |= (var->sync & FB_SYNC_SHARP_MODE) ? 0x00000040 : 0; pcr |= (var->sync & FB_SYNC_OE_ACT_HIGH) ? 0 : 0x00100000; __raw_writel(pcr, LCDC_REG(LCDC_LPCR)); /* Horizontal and vertical configuration register */ __raw_writel(((var->hsync_len - 1) << 26) + ((var->right_margin - 1) << 8) + (var->left_margin - 3), LCDC_REG(LCDC_LHCR)); __raw_writel((var->vsync_len << 26) + (var->lower_margin << 8) + var->upper_margin, LCDC_REG(LCDC_LVCR)); /* Sharp configuration register */ __raw_writel(0x00120300, LCDC_REG(LCDC_LSCR)); /* Refresh mode control reigster */ __raw_writel(0x00000000, LCDC_REG(LCDC_LRMCR)); /* DMA control register */ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) __raw_writel(0x00040060, LCDC_REG(LCDC_LDCR)); else __raw_writel(0x00020010, LCDC_REG(LCDC_LDCR)); }
/*! * @brief Set LCD brightness * @param level brightness level */ static void _set_brightness(unsigned char level) { /* Set LCDC PWM contract control register */ __raw_writel(0x00A90300 | level, LCDC_REG(LCDC_LPCCR)); }