예제 #1
0
static void atmel_lcdfb_reset(struct atmel_lcdfb_info *sinfo)
{
	might_sleep();

	atmel_lcdfb_stop(sinfo);
	atmel_lcdfb_start(sinfo);
}
예제 #2
0
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;
}
예제 #3
0
/**
 *      atmel_lcdfb_set_par - Alters the hardware state.
 *      @info: frame buffer structure that represents a single frame buffer
 *
 *	Using the fb_var_screeninfo in fb_info we set the resolution
 *	of the this particular framebuffer. This function alters the
 *	par AND the fb_fix_screeninfo stored in fb_info. It doesn't
 *	not alter var in fb_info since we are using that data. This
 *	means we depend on the data in var inside fb_info to be
 *	supported by the hardware.  atmel_lcdfb_check_var is always called
 *	before atmel_lcdfb_set_par to ensure this.  Again if you can't
 *	change the resolution you don't need this function.
 *
 */
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;

    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);

    /* Re-initialize the DMA engine... */
    dev_dbg(info->device, "  * update DMA engine\n");
    atmel_lcdfb_update_dma(info, &info->var);

    /* ...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);

    /* Now, the LCDC core... */

    /* Set pixel clock */
    clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000;

    value = DIV_ROUND_UP(clk_value_khz, PICOS2KHZ(info->var.pixclock));

    if (value < 2) {
        dev_notice(info->device, "Bypassing pixel clock divider\n");
        lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS);
    } else {
        value = (value / 2) - 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 / (2 * (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);

    atmel_lcdfb_start(sinfo);

    dev_dbg(info->device, "  * DONE\n");

    return 0;
}