void __init at91_add_device_lcdc(struct atmel_lcdfb_pdata *data) { if (!data) return; if (cpu_is_at91sam9g45es()) at91_lcdc_device.name = "at91sam9g45es-lcdfb"; else at91_lcdc_device.name = "at91sam9g45-lcdfb"; at91_set_A_periph(AT91_PIN_PE0, 0); /* LCDDPWR */ at91_set_A_periph(AT91_PIN_PE2, 0); /* LCDCC */ at91_set_A_periph(AT91_PIN_PE3, 0); /* LCDVSYNC */ at91_set_A_periph(AT91_PIN_PE4, 0); /* LCDHSYNC */ at91_set_A_periph(AT91_PIN_PE5, 0); /* LCDDOTCK */ at91_set_A_periph(AT91_PIN_PE6, 0); /* LCDDEN */ at91_set_A_periph(AT91_PIN_PE7, 0); /* LCDD0 */ at91_set_A_periph(AT91_PIN_PE8, 0); /* LCDD1 */ at91_set_A_periph(AT91_PIN_PE9, 0); /* LCDD2 */ at91_set_A_periph(AT91_PIN_PE10, 0); /* LCDD3 */ at91_set_A_periph(AT91_PIN_PE11, 0); /* LCDD4 */ at91_set_A_periph(AT91_PIN_PE12, 0); /* LCDD5 */ at91_set_A_periph(AT91_PIN_PE13, 0); /* LCDD6 */ at91_set_A_periph(AT91_PIN_PE14, 0); /* LCDD7 */ at91_set_A_periph(AT91_PIN_PE15, 0); /* LCDD8 */ at91_set_A_periph(AT91_PIN_PE16, 0); /* LCDD9 */ at91_set_A_periph(AT91_PIN_PE17, 0); /* LCDD10 */ at91_set_A_periph(AT91_PIN_PE18, 0); /* LCDD11 */ at91_set_A_periph(AT91_PIN_PE19, 0); /* LCDD12 */ at91_set_A_periph(AT91_PIN_PE20, 0); /* LCDD13 */ at91_set_A_periph(AT91_PIN_PE21, 0); /* LCDD14 */ at91_set_A_periph(AT91_PIN_PE22, 0); /* LCDD15 */ at91_set_A_periph(AT91_PIN_PE23, 0); /* LCDD16 */ at91_set_A_periph(AT91_PIN_PE24, 0); /* LCDD17 */ at91_set_A_periph(AT91_PIN_PE25, 0); /* LCDD18 */ at91_set_A_periph(AT91_PIN_PE26, 0); /* LCDD19 */ at91_set_A_periph(AT91_PIN_PE27, 0); /* LCDD20 */ at91_set_A_periph(AT91_PIN_PE28, 0); /* LCDD21 */ at91_set_A_periph(AT91_PIN_PE29, 0); /* LCDD22 */ at91_set_A_periph(AT91_PIN_PE30, 0); /* LCDD23 */ lcdc_data = *data; platform_device_register(&at91_lcdc_device); }
static int atmel_lcdfb_set_par(struct fb_info *info) { struct atmel_lcdfb_info *sinfo = info->par; unsigned long hozval_linesz; unsigned long value; unsigned long clk_value_khz; unsigned long bits_per_line; unsigned long pix_factor = 2; might_sleep(); dev_dbg(info->device, "%s:\n", __func__); dev_dbg(info->device, " * resolution: %ux%u (%ux%u virtual)\n", info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual); atmel_lcdfb_stop_nowait(sinfo); if (info->var.bits_per_pixel == 1) info->fix.visual = FB_VISUAL_MONO01; else if (info->var.bits_per_pixel <= 8) info->fix.visual = FB_VISUAL_PSEUDOCOLOR; else info->fix.visual = FB_VISUAL_TRUECOLOR; bits_per_line = info->var.xres_virtual * info->var.bits_per_pixel; info->fix.line_length = DIV_ROUND_UP(bits_per_line, 8); dev_dbg(info->device, " * update DMA engine\n"); atmel_lcdfb_update_dma(info, &info->var); value = (info->var.yres * info->var.xres * info->var.bits_per_pixel) / 32; value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET); lcdc_writel(sinfo, ATMEL_LCDC_DMAFRMCFG, value); if (cpu_is_at91sam9g45() && !cpu_is_at91sam9g45es()) pix_factor = 1; clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; value = DIV_ROUND_UP(clk_value_khz, PICOS2KHZ(info->var.pixclock)); if (value < pix_factor) { dev_notice(info->device, "Bypassing pixel clock divider\n"); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS); } else { value = (value / pix_factor) - 1; dev_dbg(info->device, " * programming CLKVAL = 0x%08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, value << ATMEL_LCDC_CLKVAL_OFFSET); info->var.pixclock = KHZ2PICOS(clk_value_khz / (pix_factor * (value + 1))); dev_dbg(info->device, " updated pixclk: %lu KHz\n", PICOS2KHZ(info->var.pixclock)); } value = sinfo->default_lcdcon2; if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) value |= ATMEL_LCDC_INVLINE_INVERTED; if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) value |= ATMEL_LCDC_INVFRAME_INVERTED; switch (info->var.bits_per_pixel) { case 1: value |= ATMEL_LCDC_PIXELSIZE_1; break; case 2: value |= ATMEL_LCDC_PIXELSIZE_2; break; case 4: value |= ATMEL_LCDC_PIXELSIZE_4; break; case 8: value |= ATMEL_LCDC_PIXELSIZE_8; break; case 15: case 16: value |= ATMEL_LCDC_PIXELSIZE_16; break; case 24: value |= ATMEL_LCDC_PIXELSIZE_24; break; case 32: value |= ATMEL_LCDC_PIXELSIZE_32; break; default: BUG(); break; } dev_dbg(info->device, " * LCDCON2 = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON2, value); value = (info->var.vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET; value |= info->var.upper_margin << ATMEL_LCDC_VBP_OFFSET; value |= info->var.lower_margin; dev_dbg(info->device, " * LCDTIM1 = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_TIM1, value); value = (info->var.right_margin - 1) << ATMEL_LCDC_HFP_OFFSET; value |= (info->var.hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET; value |= (info->var.left_margin - 1); dev_dbg(info->device, " * LCDTIM2 = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value); hozval_linesz = compute_hozval(info->var.xres, lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2)); value = (hozval_linesz - 1) << ATMEL_LCDC_HOZVAL_OFFSET; value |= info->var.yres - 1; dev_dbg(info->device, " * LCDFRMCFG = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_LCDFRMCFG, value); value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3); lcdc_writel(sinfo, ATMEL_LCDC_FIFO, value); lcdc_writel(sinfo, ATMEL_LCDC_MVAL, 0); lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); lcdc_writel(sinfo, ATMEL_LCDC_IER, ATMEL_LCDC_UFLWI | ATMEL_LCDC_OWRI | ATMEL_LCDC_MERI); while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) msleep(10); atmel_lcdfb_start(sinfo); dev_dbg(info->device, " * DONE\n"); return 0; }
static int atmel_lcdfb_setup_core(struct fb_info *info) { struct atmel_lcdfb_info *sinfo = info->par; unsigned long hozval_linesz; unsigned long value; unsigned long clk_value_khz; unsigned long pix_factor = 2; /* ...set frame size and burst length = 8 words (?) */ value = (info->var.yres * info->var.xres * info->var.bits_per_pixel) / 32; value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET); lcdc_writel(sinfo, ATMEL_LCDC_DMAFRMCFG, value); /* Set pixel clock */ if (cpu_is_at91sam9g45() && !cpu_is_at91sam9g45es()) pix_factor = 1; clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; value = DIV_ROUND_UP(clk_value_khz, PICOS2KHZ(info->var.pixclock)); if (value < pix_factor) { dev_notice(info->device, "Bypassing pixel clock divider\n"); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS); } else { value = (value / pix_factor) - 1; dev_dbg(info->device, " * programming CLKVAL = 0x%08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, value << ATMEL_LCDC_CLKVAL_OFFSET); info->var.pixclock = KHZ2PICOS(clk_value_khz / (pix_factor * (value + 1))); dev_dbg(info->device, " updated pixclk: %lu KHz\n", PICOS2KHZ(info->var.pixclock)); } /* Initialize control register 2 */ value = sinfo->default_lcdcon2; if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) value |= ATMEL_LCDC_INVLINE_INVERTED; if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) value |= ATMEL_LCDC_INVFRAME_INVERTED; switch (info->var.bits_per_pixel) { case 1: value |= ATMEL_LCDC_PIXELSIZE_1; break; case 2: value |= ATMEL_LCDC_PIXELSIZE_2; break; case 4: value |= ATMEL_LCDC_PIXELSIZE_4; break; case 8: value |= ATMEL_LCDC_PIXELSIZE_8; break; case 15: /* fall through */ case 16: value |= ATMEL_LCDC_PIXELSIZE_16; break; case 24: value |= ATMEL_LCDC_PIXELSIZE_24; break; case 32: value |= ATMEL_LCDC_PIXELSIZE_32; break; default: BUG(); break; } dev_dbg(info->device, " * LCDCON2 = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON2, value); /* Vertical timing */ value = (info->var.vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET; value |= info->var.upper_margin << ATMEL_LCDC_VBP_OFFSET; value |= info->var.lower_margin; dev_dbg(info->device, " * LCDTIM1 = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_TIM1, value); /* Horizontal timing */ value = (info->var.right_margin - 1) << ATMEL_LCDC_HFP_OFFSET; value |= (info->var.hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET; value |= (info->var.left_margin - 1); dev_dbg(info->device, " * LCDTIM2 = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value); /* Horizontal value (aka line size) */ hozval_linesz = compute_hozval(info->var.xres, lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2)); /* Display size */ value = (hozval_linesz - 1) << ATMEL_LCDC_HOZVAL_OFFSET; value |= info->var.yres - 1; dev_dbg(info->device, " * LCDFRMCFG = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_LCDFRMCFG, value); /* FIFO Threshold: Use formula from data sheet */ value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3); lcdc_writel(sinfo, ATMEL_LCDC_FIFO, value); /* Toggle LCD_MODE every frame */ lcdc_writel(sinfo, ATMEL_LCDC_MVAL, 0); /* Disable all interrupts */ lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); /* Enable FIFO & DMA errors */ lcdc_writel(sinfo, ATMEL_LCDC_IER, ATMEL_LCDC_UFLWI | ATMEL_LCDC_OWRI | ATMEL_LCDC_MERI); /* ...wait for DMA engine to become idle... */ while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) msleep(10); return 0; }